diff --git a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h index 244d3d19d..e31fff427 100644 --- a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h +++ b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h @@ -26,6 +26,7 @@ class ChainedArenaAllocator ~ChainedArenaAllocator() { + free_extra_allocs(); free_arenas(); } @@ -35,8 +36,9 @@ class ChainedArenaAllocator */ void reset (size_t head_arena_size_bytes) { - arena_size_bytes = head_arena_size_bytes; + arena_size_bytes = std::max (head_arena_size_bytes, (size_t) 32); + free_extra_allocs(); free_arenas(); current_arena = bootstrap_arena (arena_size_bytes); @@ -50,6 +52,7 @@ class ChainedArenaAllocator */ void clear() noexcept { + free_extra_allocs(); current_arena = arena_list.head; get_current_arena().clear(); } @@ -59,8 +62,16 @@ class ChainedArenaAllocator { if (num_bytes > arena_size_bytes) { - jassertfalse; - return nullptr; + auto* extra_alloc = allocate (1); + extra_alloc->ptr = aligned_alloc (std::max (alignment, (size_t) 8), num_bytes); + extra_alloc->size = num_bytes; + if (extra_alloc_list != nullptr) + extra_alloc->next = extra_alloc_list; + else + extra_alloc->next = nullptr; + extra_alloc_list = extra_alloc; + + return extra_alloc->ptr; } auto pointer = get_current_arena().allocate_bytes (num_bytes, alignment); @@ -119,6 +130,10 @@ class ChainedArenaAllocator for (auto* arena = arena_list.head; arena != current_arena; arena = arena->next) bytes_count += arena->get_bytes_used(); bytes_count += current_arena->get_bytes_used(); + + for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next) + bytes_count += extra_alloc->size; + return bytes_count; } @@ -131,25 +146,29 @@ class ChainedArenaAllocator size_t bytes_count = 0; for (auto* arena = arena_list.head; arena != nullptr; arena = arena->next) bytes_count += arena->get_total_num_bytes(); + for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next) + bytes_count += extra_alloc->size; return bytes_count; } /** Merges another allocator into this one, and invalidates the other allocator. */ void merge (ChainedArenaAllocator& allocator_to_merge) { - if (allocator_to_merge.arena_list.count == 0) + if (allocator_to_merge.arena_list.count == 0 && allocator_to_merge.extra_alloc_list == nullptr) return; // no work to do! // both arenas must have the same head size! jassert (arena_size_bytes == 0 || arena_size_bytes == allocator_to_merge.arena_size_bytes); // if our arena is empty, just make this arena into the other arena! - if (arena_list.count == 0) + if (arena_list.count == 0 && extra_alloc_list == nullptr) { current_arena = allocator_to_merge.current_arena; arena_list = allocator_to_merge.arena_list; + extra_alloc_list = allocator_to_merge.extra_alloc_list; allocator_to_merge.current_arena = nullptr; allocator_to_merge.arena_list = {}; + allocator_to_merge.extra_alloc_list = nullptr; return; } @@ -172,8 +191,18 @@ class ChainedArenaAllocator arena_list.count += arena_add_count; + if (allocator_to_merge.extra_alloc_list != nullptr) + { + ExtraAlloc* end_of_list = allocator_to_merge.extra_alloc_list; + while (end_of_list->next != nullptr) + end_of_list = end_of_list->next; + end_of_list->next = extra_alloc_list; + extra_alloc_list = allocator_to_merge.extra_alloc_list; + } + allocator_to_merge.current_arena = nullptr; allocator_to_merge.arena_list = {}; + allocator_to_merge.extra_alloc_list = nullptr; allocator_to_merge = {}; } @@ -241,6 +270,13 @@ class ChainedArenaAllocator } } + void free_extra_allocs() + { + for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next) + aligned_free (extra_alloc->ptr); + extra_alloc_list = nullptr; + } + static ArenaNode* bootstrap_arena (size_t num_bytes) { static constexpr size_t arena_alignment = 64; @@ -273,5 +309,13 @@ class ChainedArenaAllocator ArenaList arena_list {}; ArenaNode* current_arena {}; size_t arena_size_bytes = 0; + + struct ExtraAlloc + { + void* ptr = nullptr; + size_t size {}; + ExtraAlloc* next = nullptr; + }; + ExtraAlloc* extra_alloc_list = nullptr; }; } // namespace chowdsp diff --git a/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp b/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp index f2545aacb..b65eab509 100644 --- a/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp @@ -22,9 +22,6 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]") REQUIRE (allocator.get_total_bytes_used() == 160); } - // overfull allocation - REQUIRE (allocator.template allocate (200) == nullptr); - // clear allocator allocator.clear(); REQUIRE (allocator.get_arena_count() == 2); @@ -47,6 +44,15 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]") } } + SECTION ("Overful Allocation") + { + REQUIRE (allocator.template allocate (200) != nullptr); + REQUIRE (allocator.template allocate (200) != nullptr); + REQUIRE (allocator.get_total_bytes_used() == 3200 + 2 * 24); + REQUIRE (allocator.get_total_bytes() == 3200 + 144); + allocator.clear(); + } + SECTION ("Usage with Frame") { { @@ -89,6 +95,8 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]") merge_alloc.merge (alloc1); alloc2.allocate (24); alloc2.allocate (24); + alloc2.allocate (48); + alloc2.allocate (48); merge_alloc.merge (alloc2); merge_alloc.merge (alloc3); REQUIRE (merge_alloc.get_arena_count() == arena_count); diff --git a/tests/common_tests/chowdsp_logging_test/CustomFormattingTest.cpp b/tests/common_tests/chowdsp_logging_test/CustomFormattingTest.cpp index 95362b181..cbb9ffe4e 100644 --- a/tests/common_tests/chowdsp_logging_test/CustomFormattingTest.cpp +++ b/tests/common_tests/chowdsp_logging_test/CustomFormattingTest.cpp @@ -3,7 +3,7 @@ TEST_CASE ("Custom Formatting Test", "[common][logs]") { - chowdsp::ArenaAllocator<> arena { 2048 }; + chowdsp::ChainedArenaAllocator arena { 2048 }; SECTION ("juce::String") { @@ -18,4 +18,11 @@ TEST_CASE ("Custom Formatting Test", "[common][logs]") const auto format_result = chowdsp::format (arena, "{}", nonstd::span { vec }); REQUIRE (format_result == "{0,1,2,3,4}"); } + + SECTION ("Large String") + { + std::string large_string { "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ipsum enim, bibendum venenatis sollicitudin eget, pretium at sem. Vivamus efficitur risus eget nulla tempus, eget lacinia orci dictum. Sed egestas nulla accumsan sodales finibus. Phasellus faucibus, sapien at aliquam gravida, turpis mi pellentesque mauris, et bibendum purus turpis sit amet elit. In in metus ac quam dictum sodales. Integer odio ex, mattis ac pulvinar vitae, congue vitae tortor. Duis porta vehicula consequat. Nullam pellentesque malesuada massa, id fermentum metus ultrices condimentum. Quisque felis mauris, vulputate nec luctus sed, suscipit in justo. Quisque et porta est. In in congue arcu. Donec posuere rutrum sapien, nec efficitur velit ultricies id. Vivamus eget sollicitudin leo. Donec accumsan placerat risus, eget congue enim egestas sit amet. Aliquam erat volutpat. Ut vitae dignissim elit. Integer dignissim id leo eu semper. Nullam pulvinar ac eros et hendrerit. Maecenas imperdiet, odio sit amet cursus maximus, urna augue tempus orci, fermentum porta magna sem id tortor. Sed consectetur, arcu quis placerat eleifend, augue dolor egestas tellus, in tempor eros ante quis urna. Phasellus id eleifend justo, vitae viverra tortor. Phasellus tincidunt porttitor auctor. Fusce a velit quis orci ullamcorper blandit sit amet vel arcu. Ut venenatis mauris ac augue scelerisque, sit amet malesuada lacus aliquam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed nec magna at ipsum suscipit eleifend sit amet ac sapien. Nunc ut metus erat. Suspendisse nisl mauris, accumsan sed scelerisque et, mattis eget sem. Aliquam pulvinar metus a scelerisque vulputate. Etiam non risus risus. Sed ornare nunc fringilla consequat commodo. Nam vitae egestas enim. Duis pretium leo et felis aliquam, ullamcorper pellentesque neque congue. Vestibulum posuere massa vel purus efficitur accumsan. Integer ut metus eget elit viverra blandit. Nulla at tincidunt justo, a ornare nunc. Pellentesque scelerisque eros vitae magna posuere volutpat. Pellentesque euismod nunc vitae odio aliquam, non gravida tellus dignissim. Duis semper eu ligula at sollicitudin. Nullam euismod, diam et mollis consectetur, mauris ex facilisis dolor, nec fringilla massa nulla a dui. Curabitur lacus purus, bibendum ac justo vestibulum, tempus gravida massa. Donec quis condimentum quam. Morbi eleifend mi lectus, quis convallis ante vestibulum eget. Praesent eu metus non mauris scelerisque malesuada. Vivamus malesuada elit ac lacus fermentum sodales. Nullam blandit leo nulla, et condimentum velit bibendum pellentesque. Fusce felis risus, rhoncus eu tempus sed, euismod ut eros. Praesent sagittis placerat nisl nec aliquet. Fusce id eros diam. Nullam in tincidunt lectus. Vestibulum at finibus sem. Suspendisse eleifend mauris nec nulla ornare lacinia in vel felis. Sed finibus tempor pharetra. Aliquam efficitur aliquam suscipit. Phasellus commodo euismod posuere. Phasellus pharetra justo quam, eu auctor orci laoreet quis." }; + const auto format_result = chowdsp::format (arena, "{}", large_string); + REQUIRE (format_result == large_string); + } }