diff --git a/CMakeLists.txt b/CMakeLists.txt index d2c5d293e9..c4b6da4810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,8 @@ add_executable(deluge) # Link external libraries target_link_libraries(deluge PUBLIC - argon + argon + etl::etl ) option(MAINTAINER_MODE "Enables all warnings and errors" OFF) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 16e4d1736e..292a164b32 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,8 +3,15 @@ cmake_policy(SET CMP0135 NEW) include(FetchContent) FetchContent_Declare(argon - GIT_REPOSITORY https://github.com/stellar-aria/argon - GIT_TAG 3779c5315a978a2f8c892e320ac12ecbb77c3759 + GIT_REPOSITORY https://github.com/stellar-aria/argon + GIT_TAG 3779c5315a978a2f8c892e320ac12ecbb77c3759 ) FetchContent_MakeAvailable(argon) +FetchContent_Declare( + etl + GIT_REPOSITORY https://github.com/ETLCPP/etl + GIT_TAG 20.39.4 +) + +FetchContent_MakeAvailable(etl) \ No newline at end of file diff --git a/src/OSLikeStuff/task_scheduler/task_scheduler.cpp b/src/OSLikeStuff/task_scheduler/task_scheduler.cpp index 7f8e3ef123..6a6dd228dc 100644 --- a/src/OSLikeStuff/task_scheduler/task_scheduler.cpp +++ b/src/OSLikeStuff/task_scheduler/task_scheduler.cpp @@ -18,7 +18,6 @@ #include "OSLikeStuff/task_scheduler/task_scheduler.h" #include "io/debug/log.h" -#include "util/container/static_vector.hpp" #include #include diff --git a/src/deluge/gui/menu_item/active_scales.cpp b/src/deluge/gui/menu_item/active_scales.cpp index 28e8c55e8a..70eccd4569 100644 --- a/src/deluge/gui/menu_item/active_scales.cpp +++ b/src/deluge/gui/menu_item/active_scales.cpp @@ -8,9 +8,10 @@ #include "model/song/song.h" #include "source_selection/range.h" #include "source_selection/regular.h" -#include "util/container/static_vector.hpp" #include "util/functions.h" +#include + namespace deluge::gui::menu_item { void ActiveScaleMenu::beginSession(MenuItem* navigatedBackwardFrom) { @@ -36,7 +37,7 @@ void ActiveScaleMenu::drawPixelsForOled() { uint8_t sel; // Build a vector with visible scale items. No wrap-around. - static_vector visible = {}; + etl::vector visible = {}; if (currentPos == 0) { sel = 0; // beginning of the list diff --git a/src/deluge/gui/menu_item/dx/cartridge.cpp b/src/deluge/gui/menu_item/dx/cartridge.cpp index 431170b465..7c460f11ab 100644 --- a/src/deluge/gui/menu_item/dx/cartridge.cpp +++ b/src/deluge/gui/menu_item/dx/cartridge.cpp @@ -28,9 +28,9 @@ #include "processing/sound/sound.h" #include "processing/source.h" #include "storage/DX7Cartridge.h" -#include "util/container/static_vector.hpp" #include "util/functions.h" #include "util/try.h" +#include #include static bool openFile(std::string_view path, DX7Cartridge* data) { @@ -116,7 +116,7 @@ void DxCartridge::drawPixelsForOled() { char names[32][11]; pd->getProgramNames(names); - static_vector itemNames = {}; + etl::vector itemNames = {}; for (int i = 0; i < pd->numPatches(); i++) { itemNames.push_back(names[i]); } diff --git a/src/deluge/gui/menu_item/dx/engine_select.cpp b/src/deluge/gui/menu_item/dx/engine_select.cpp index 51d958d156..945bd54d5c 100644 --- a/src/deluge/gui/menu_item/dx/engine_select.cpp +++ b/src/deluge/gui/menu_item/dx/engine_select.cpp @@ -23,7 +23,8 @@ #include "hid/display/display.h" #include "memory/general_memory_allocator.h" #include "processing/source.h" -#include "util/container/static_vector.hpp" + +#include namespace deluge::gui::menu_item { @@ -42,7 +43,7 @@ void DxEngineSelect::readValueAgain() { constexpr int numValues = 3; void DxEngineSelect::drawPixelsForOled() { - static_vector itemNames = {"auto", "modern", "vintage"}; + etl::vector itemNames = {"auto", "modern", "vintage"}; drawItemsForOled(itemNames, currentValue, 0); } diff --git a/src/deluge/gui/menu_item/dx/global_params.cpp b/src/deluge/gui/menu_item/dx/global_params.cpp index f33cecc3a7..5026c934e9 100644 --- a/src/deluge/gui/menu_item/dx/global_params.cpp +++ b/src/deluge/gui/menu_item/dx/global_params.cpp @@ -24,7 +24,8 @@ #include "hid/display/display.h" #include "memory/general_memory_allocator.h" #include "processing/source.h" -#include "util/container/static_vector.hpp" + +#include namespace deluge::gui::menu_item { @@ -57,7 +58,7 @@ struct { constexpr int numValues = sizeof(items) / sizeof(items[0]); void DxGlobalParams::drawPixelsForOled() { - static_vector itemNames = {}; + etl::vector itemNames = {}; for (auto& item : items) { itemNames.push_back(item.name); } diff --git a/src/deluge/gui/menu_item/dx/operator_params.cpp b/src/deluge/gui/menu_item/dx/operator_params.cpp index 3f0fdec9bc..ecd2fe55a8 100644 --- a/src/deluge/gui/menu_item/dx/operator_params.cpp +++ b/src/deluge/gui/menu_item/dx/operator_params.cpp @@ -23,7 +23,7 @@ #include "hid/display/display.h" #include "memory/general_memory_allocator.h" #include "processing/source.h" -#include "util/container/static_vector.hpp" +#include namespace deluge::gui::menu_item { @@ -60,7 +60,7 @@ struct { constexpr int numValues = sizeof(items) / sizeof(items[0]); void DxOperatorParams::drawPixelsForOled() { - static_vector itemNames = {}; + etl::vector itemNames = {}; for (auto& item : items) { itemNames.push_back(item.name); } diff --git a/src/deluge/gui/menu_item/midi/devices.cpp b/src/deluge/gui/menu_item/midi/devices.cpp index 0e5412df7e..4f029b54c8 100644 --- a/src/deluge/gui/menu_item/midi/devices.cpp +++ b/src/deluge/gui/menu_item/midi/devices.cpp @@ -25,7 +25,7 @@ #include "io/midi/cable_types/usb_hosted.h" #include "io/midi/midi_device.h" #include "io/midi/midi_device_manager.h" -#include "util/container/static_vector.hpp" +#include #include extern deluge::gui::menu_item::midi::Device midiDeviceMenu; @@ -150,7 +150,7 @@ MenuItem* Devices::selectButtonPress() { } void Devices::drawPixelsForOled() { - static_vector itemNames = {}; + etl::vector itemNames = {}; int32_t selectedRow = -1; diff --git a/src/deluge/gui/menu_item/multi_range.cpp b/src/deluge/gui/menu_item/multi_range.cpp index 6590303c7c..fbfb9dadce 100644 --- a/src/deluge/gui/menu_item/multi_range.cpp +++ b/src/deluge/gui/menu_item/multi_range.cpp @@ -27,9 +27,9 @@ #include "processing/sound/sound.h" #include "processing/source.h" #include "storage/multi_range/multisample_range.h" -#include "util/container/static_vector.hpp" #include "util/functions.h" #include +#include namespace deluge::gui::menu_item { @@ -425,7 +425,7 @@ bool MultiRange::mayEditRangeEdge(RangeEdit whichEdge) { } void MultiRange::drawPixelsForOled() { - static_vector itemNames{}; + etl::vector itemNames{}; char nameBuffers[kOLEDMenuNumOptionsVisible][20]; int32_t actualCurrentRange = this->getValue(); diff --git a/src/deluge/gui/menu_item/source_selection.cpp b/src/deluge/gui/menu_item/source_selection.cpp index e7e1d2bbea..64a1130b9a 100644 --- a/src/deluge/gui/menu_item/source_selection.cpp +++ b/src/deluge/gui/menu_item/source_selection.cpp @@ -23,7 +23,8 @@ #include "modulation/params/param_manager.h" #include "modulation/patch/patch_cable_set.h" #include "processing/sound/sound.h" -#include "util/container/static_vector.hpp" + +#include namespace deluge::gui::menu_item { const PatchSource sourceMenuContents[] = { @@ -43,7 +44,7 @@ uint8_t SourceSelection::shouldDrawDotOnValue() { int32_t SourceSelection::selectedRowOnScreen; void SourceSelection::drawPixelsForOled() { - static_vector itemNames{}; + etl::vector itemNames{}; selectedRowOnScreen = 0; diff --git a/src/deluge/gui/menu_item/submenu.cpp b/src/deluge/gui/menu_item/submenu.cpp index 7d4a7e7ff6..3a57fd6400 100644 --- a/src/deluge/gui/menu_item/submenu.cpp +++ b/src/deluge/gui/menu_item/submenu.cpp @@ -1,9 +1,9 @@ #include "submenu.h" +#include "etl/vector.h" #include "gui/views/automation_view.h" #include "hid/display/display.h" #include "hid/display/oled.h" #include "model/settings/runtime_feature_settings.h" -#include "util/container/static_vector.hpp" namespace deluge::gui::menu_item { void Submenu::beginSession(MenuItem* navigatedBackwardFrom) { @@ -57,7 +57,7 @@ void Submenu::drawPixelsForOled() { void Submenu::drawVerticalMenu() { // Collect items before the current item, this is possibly more than we need. - static_vector before = {}; + etl::vector before = {}; for (auto it = current_item_ - 1; it != items.begin() - 1 && before.size() < before.capacity(); it--) { MenuItem* menuItem = (*it); if (menuItem->isRelevant(soundEditor.currentModControllable, soundEditor.currentSourceIndex)) { @@ -67,7 +67,7 @@ void Submenu::drawVerticalMenu() { std::reverse(before.begin(), before.end()); // Collect current item and fill the tail - static_vector after = {}; + etl::vector after = {}; for (auto it = current_item_; it != items.end() && after.size() < after.capacity(); it++) { MenuItem* menuItem = (*it); if (menuItem->isRelevant(soundEditor.currentModControllable, soundEditor.currentSourceIndex)) { @@ -89,7 +89,7 @@ void Submenu::drawVerticalMenu() { } // Put it together. - static_vector visible; + etl::vector visible; visible.insert(visible.begin(), before.end() - pos, before.end()); visible.insert(visible.begin() + pos, after.begin(), after.begin() + tail); diff --git a/src/deluge/util/container/static_vector.hpp b/src/deluge/util/container/static_vector.hpp deleted file mode 100644 index 03e1c35b22..0000000000 --- a/src/deluge/util/container/static_vector.hpp +++ /dev/null @@ -1,1016 +0,0 @@ -#pragma once -/// \file -/// -/// Dynamically-resizable vector with fixed-capacity. -/// -/// Copyright Gonzalo Brito Gadeschi 2015-2017 -/// Copyright Eric Niebler 2013-2014 -/// Copyright Casey Carter 2016 -/// -/// This file is released under the Boost Software License: -// -// Boost Software License - Version 1.0 - August 17th, 2003 -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// Some of the code has been adapted from the range-v3 library: -// -// https://github.com/ericniebler/range-v3/ -// -// which is also under the Boost Software license. -// -// Some of the code has been adapted from libc++: -// -// and is annotated with "adapted from libc++" below, and is thus under the -// following license: -// -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -#include -#include -#include // for size_t -#include // for fixed-width integer types -#include // for less and equal_to -#include // for reverse_iterator and iterator traits -#include // for numeric_limits -#include // for aligned_storage and all meta-functions - -/// Optimizer allowed to assume that EXPR evaluates to true -#define SV_ASSUME(EXPR) static_cast((EXPR) ? void(0) : __builtin_unreachable()) - -/// Assert pretty printer -#define SV_ASSERT(...) \ - static_cast((__VA_ARGS__) \ - ? void(0) \ - : ::deluge::sv_detail::assert_failure(static_cast(__FILE__), __LINE__, \ - "assertion failed: " #__VA_ARGS__)) - -/// Expect asserts the condition in debug builds and assumes the condition to be -/// true in release builds. -#if defined(NDEBUG) || 1 -#define SV_EXPECT(EXPR) SV_ASSUME(EXPR) -#else -#define SV_EXPECT(EXPR) SV_ASSERT(EXPR) -#endif - -namespace deluge { -// Private utilites (each std lib should already have this) -namespace sv_detail { -/// \name Utilities -///@{ - -template -[[noreturn]] void assert_failure(char const* file, int line, char const* msg) { - printf("%s(%d): %s\n", file, line, msg); - abort(); -} - -template -using range_iterator_t = decltype(std::begin(std::declval())); - -template -using iterator_reference_t = typename std::iterator_traits::reference; - -// clang-format off - -/// Smallest fixed-width unsigned integer type that can represent -/// values in the range [0, N]. -template -using smallest_size_t - = std::conditional_t<(N < std::numeric_limits::max()), uint8_t, - std::conditional_t<(N < std::numeric_limits::max()), uint16_t, - std::conditional_t<(N < std::numeric_limits::max()), uint32_t, - std::conditional_t<(N < std::numeric_limits::max()), uint64_t, - size_t>>>>; -// clang-format on - -///@} // Utilities - -/// Types implementing the `fixed_capactiy_vector`'s storage -namespace storage { -/// Storage for zero elements. -template -struct zero_sized { - using size_type = uint8_t; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using const_pointer = T const*; - - /// Pointer to the data in the storage. - static constexpr pointer data() noexcept { return nullptr; } - /// Number of elements currently stored. - static constexpr size_type size() noexcept { return 0; } - /// Capacity of the storage. - static constexpr size_type capacity() noexcept { return 0; } - /// Is the storage empty? - static constexpr bool empty() noexcept { return true; } - /// Is the storage full? - static constexpr bool full() noexcept { return true; } - - /// Constructs a new element at the end of the storage - /// in-place. - /// - /// Increases size of the storage by one. - /// Always fails for empty storage. - template - requires std::constructible_from - static constexpr void emplace_back(Args&&...) noexcept { - SV_EXPECT(false && "tried to emplace_back on empty storage"); - } - /// Removes the last element of the storage. - /// Always fails for empty storage. - static constexpr void pop_back() noexcept { SV_EXPECT(false && "tried to pop_back on empty storage"); } - /// Changes the size of the storage without adding or - /// removing elements (unsafe). - /// - /// The size of an empty storage can only be changed to 0. - static constexpr void unsafe_set_size(size_t new_size) noexcept { - SV_EXPECT(new_size == 0 - && "tried to change size of empty storage to " - "non-zero value"); - } - - /// Destroys all elements of the storage in range [begin, - /// end) without changings its size (unsafe). - /// - /// Nothing to destroy since the storage is empty. - template - static constexpr void unsafe_destroy(InputIt /* begin */, InputIt /* end */) noexcept {} - - /// Destroys all elements of the storage without changing - /// its size (unsafe). - /// - /// Nothing to destroy since the storage is empty. - static constexpr void unsafe_destroy_all() noexcept {} - - constexpr zero_sized() = default; - constexpr zero_sized(zero_sized const&) = default; - constexpr zero_sized& operator=(zero_sized const&) = default; - constexpr zero_sized(zero_sized&&) = default; - constexpr zero_sized& operator=(zero_sized&&) = default; - ~zero_sized() = default; - - /// Constructs an empty storage from an initializer list of - /// zero elements. - template - requires std::convertible_to - constexpr zero_sized(std::initializer_list il) noexcept { - SV_EXPECT(il.size() == 0 - && "tried to construct storage::empty from a " - "non-empty initializer list"); - } -}; - -/// Storage for trivial types. -template -struct trivial { - static_assert(std::is_trivial_v, "storage::trivial requires std::is_trivial_v"); - static_assert(Capacity != size_t{0}, "Capacity must be greater " - "than zero (use " - "storage::zero_sized instead)"); - - using size_type = smallest_size_t; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using const_pointer = T const*; - -private: - // If the value_type is const, make a const array of - // non-const elements: - using data_t = std::conditional_t, std::array, - const std::array, Capacity>>; - alignas(alignof(T)) data_t data_{}; - - /// Number of elements allocated in the storage: - size_type size_ = 0; - -public: - /// Direct access to the underlying storage. - /// - /// Complexity: O(1) in time and space. - constexpr const_pointer data() const noexcept { return data_.data(); } - - /// Direct access to the underlying storage. - /// - /// Complexity: O(1) in time and space. - constexpr pointer data() noexcept { return data_.data(); } - - /// Number of elements in the storage. - /// - /// Complexity: O(1) in time and space. - constexpr size_type size() const noexcept { return size_; } - - /// Maximum number of elements that can be allocated in the - /// storage. - /// - /// Complexity: O(1) in time and space. - static constexpr size_type capacity() noexcept { return Capacity; } - - /// Is the storage empty? - constexpr bool empty() const noexcept { return size() == size_type{0}; } - - /// Is the storage full? - constexpr bool full() const noexcept { return size() == Capacity; } - - /// Constructs an element in-place at the end of the - /// storage. - /// - /// Complexity: O(1) in time and space. - /// Contract: the storage is not full. - template - requires std::constructible_from && std::assignable_from - constexpr void emplace_back(Args&&... args) noexcept { - SV_EXPECT(!full() && "tried to emplace_back on full storage!"); - data_[size_++] = T(std::forward(args)...); - } - - /// Remove the last element from the container. - /// - /// Complexity: O(1) in time and space. - /// Contract: the storage is not empty. - constexpr void pop_back() noexcept { - SV_EXPECT(!empty() && "tried to pop_back from empty storage!"); - --size_; - } - - /// (unsafe) Changes the container size to \p new_size. - /// - /// Contract: `new_size <= capacity()`. - /// \warning No elements are constructed or destroyed. - constexpr void unsafe_set_size(size_t new_size) noexcept { - SV_EXPECT(new_size <= Capacity && "new_size out-of-bounds [0, Capacity]"); - size_ = size_type(new_size); - } - - /// (unsafe) Destroy elements in the range [begin, end). - /// - /// \warning: The size of the storage is not changed. - template - constexpr void unsafe_destroy(InputIt, InputIt) noexcept {} - - /// (unsafe) Destroys all elements of the storage. - /// - /// \warning: The size of the storage is not changed. - static constexpr void unsafe_destroy_all() noexcept {} - - constexpr trivial() noexcept = default; - constexpr trivial(trivial const&) noexcept = default; - constexpr trivial& operator=(trivial const&) noexcept = default; - constexpr trivial(trivial&&) noexcept = default; - constexpr trivial& operator=(trivial&&) noexcept = default; - ~trivial() = default; - -private: - template U> - static constexpr std::array, Capacity> - unsafe_recast_init_list(std::initializer_list& il) noexcept { - SV_EXPECT(il.size() <= capacity() - && "trying to construct storage from an " - "initializer_list " - "whose size exceeds the storage capacity"); - std::array, Capacity> d_{}; - for (size_t i = 0, e = il.size(); i < e; ++i) { - d_[i] = std::begin(il)[i]; - } - return d_; - } - -public: - /// Constructor from initializer list. - /// - /// Contract: `il.size() <= capacity()`. - template U> - constexpr trivial(std::initializer_list il) noexcept : data_(unsafe_recast_init_list(il)) { - unsafe_set_size(il.size()); - } -}; - -/// Storage for non-trivial elements. -template -struct non_trivial { - static_assert(!std::is_trivial_v, "use storage::trivial for std::is_trivial_v elements"); - static_assert(Capacity != size_t{0}, "Capacity must be greater than zero!"); - - /// Smallest size_type that can represent Capacity: - using size_type = smallest_size_t; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using const_pointer = T const*; - -private: - /// Number of elements allocated in the embedded storage: - size_type size_ = 0; - - // using aligned_storage_t = std::aligned_storage_t), - // alignof(std::remove_const_t)>; using data_t = std::conditional_t, aligned_storage_t, const - // aligned_storage_t>; alignas(alignof(T)) data_t data_[Capacity]{}; - // FIXME: ^ this won't work for types with "broken" alignof - // like SIMD types (one would also need to provide an - // overload of operator new to make heap allocations of this - // type work for these types). - - // Kate's solution to deal with badly aligned members - // This will be fixed with the GMA rework - std::array data_{}; - -public: - /// Direct access to the underlying storage. - /// - /// Complexity: O(1) in time and space. - const_pointer data() const noexcept { return data_.data(); } - - /// Direct access to the underlying storage. - /// - /// Complexity: O(1) in time and space. - pointer data() noexcept { return data_.data(); } - - /// Pointer to one-past-the-end. - const_pointer end() const noexcept { return data() + size(); } - - /// Pointer to one-past-the-end. - pointer end() noexcept { return data() + size(); } - - /// Number of elements in the storage. - /// - /// Complexity: O(1) in time and space. - constexpr size_type size() const noexcept { return size_; } - - /// Maximum number of elements that can be allocated in the - /// storage. - /// - /// Complexity: O(1) in time and space. - static constexpr size_type capacity() noexcept { return Capacity; } - - /// Is the storage empty? - [[nodiscard]] constexpr bool empty() const noexcept { return size() == size_type{0}; } - - /// Is the storage full? - [[nodiscard]] constexpr bool full() const noexcept { return size() == Capacity; } - - /// Constructs an element in-place at the end of the - /// embedded storage. - /// - /// Complexity: O(1) in time and space. - /// Contract: the storage is not full. - template - requires std::constructible_from - void emplace_back(Args&&... args) noexcept(noexcept(new(end()) T(std::forward(args)...))) { - SV_EXPECT(!full() && "tried to emplace_back on full storage"); - // new (end()) T(std::forward(args)...); - // unsafe_set_size(size() + 1); - - // NOTE: (Kate) Faster somehow... - void* end = &data_[size_++]; - new (end) T(std::forward(args)...); - } - - /// Remove the last element from the container. - /// - /// Complexity: O(1) in time and space. - /// Contract: the storage is not empty. - void pop_back() noexcept(std::is_nothrow_destructible_v) { - SV_EXPECT(!empty() && "tried to pop_back from empty storage!"); - - // TODO: - /// The below code commented out due to the change to std::array as a backing system for - /// data_. Once a more robust allocator with proper alignment guarantees is used, - /// we can switch back. - - // auto ptr = end() - 1; - // ptr->~T(); - // unsafe_set_size(size() - 1); - - --size_; - } - - /// (unsafe) Changes the container size to \p new_size. - /// - /// Contract: `new_size <= capacity()`. - /// \warning No elements are constructed or destroyed. - constexpr void unsafe_set_size(size_t new_size) noexcept { - SV_EXPECT(new_size <= Capacity && "new_size out-of-bounds [0, Capacity)"); - size_ = size_type(new_size); - } - - /// (unsafe) Destroy elements in the range [begin, end). - /// - /// \warning: The size of the storage is not changed. - template - void unsafe_destroy(InputIt first, InputIt last) noexcept(std::is_nothrow_destructible_v) { - SV_EXPECT(first >= data() && first <= end() && "first is out-of-bounds"); - SV_EXPECT(last >= data() && last <= end() && "last is out-of-bounds"); - // for (; first != last; ++first) { - // first->~T(); - // } - } - - /// (unsafe) Destroys all elements of the storage. - /// - /// \warning: The size of the storage is not changed. - void unsafe_destroy_all() noexcept(std::is_nothrow_destructible_v) { - - // TODO: - /// The below code commented out due to the change to std::array as a backing system for - /// data_. Once a more robust allocator with proper alignment guarantees is used, - /// we can switch back. - - // unsafe_destroy(data(), end()); - } - - constexpr non_trivial() = default; - constexpr non_trivial(non_trivial const&) = default; - constexpr non_trivial& operator=(non_trivial const&) = default; - constexpr non_trivial(non_trivial&&) = default; - constexpr non_trivial& operator=(non_trivial&&) = default; - ~non_trivial() noexcept(std::is_nothrow_destructible_v) { - // TODO: - /// The below code commented out due to the change to std::array as a backing system for - /// data_. Once a more robust allocator with proper alignment guarantees is used, - /// we can switch back. - - // unsafe_destroy_all(); - } - - /// Constructor from initializer list. - /// - /// Contract: `il.size() <= capacity()`. - template U> - constexpr non_trivial(std::initializer_list il) { - SV_EXPECT(il.size() <= capacity() - && "trying to construct storage from an " - "initializer_list " - "whose size exceeds the storage capacity"); - std::copy(il.begin(), il.end(), data_.begin()); - unsafe_set_size(il.size()); - } -}; - -/// Selects the vector storage. -template -using _t = std::conditional_t, - std::conditional_t, trivial, non_trivial>>; - -} // namespace storage - -} // namespace sv_detail - -/// Dynamically-resizable fixed-capacity vector. -template -struct static_vector : private sv_detail::storage::_t { -private: - static_assert(std::is_nothrow_destructible_v, "T must be nothrow destructible"); - using base_t = sv_detail::storage::_t; - using self = static_vector; - - using base_t::unsafe_destroy; - using base_t::unsafe_destroy_all; - using base_t::unsafe_set_size; - -public: - using value_type = typename base_t::value_type; - using difference_type = ptrdiff_t; - using reference = value_type&; - using const_reference = value_type const&; - using pointer = typename base_t::pointer; - using const_pointer = typename base_t::const_pointer; - using iterator = typename base_t::pointer; - using const_iterator = typename base_t::const_pointer; - using size_type = size_t; - using reverse_iterator = ::std::reverse_iterator; - using const_reverse_iterator = ::std::reverse_iterator; - - /// \name Size / capacity - ///@{ - using base_t::empty; - using base_t::full; - - /// Number of elements in the vector - [[nodiscard]] constexpr size_type size() const noexcept { return base_t::size(); } - - /// Maximum number of elements that can be allocated in the vector - static constexpr size_type capacity() noexcept { return base_t::capacity(); } - - /// Maximum number of elements that can be allocated in the vector - static constexpr size_type max_size() noexcept { return capacity(); } - - ///@} // Size / capacity - - /// \name Data access - ///@{ - - using base_t::data; - - ///@} // Data access - - /// \name Iterators - ///@{ - - constexpr iterator begin() noexcept { return data(); } - constexpr const_iterator begin() const noexcept { return data(); } - constexpr iterator end() noexcept { return data() + size(); } - constexpr const_iterator end() const noexcept { return data() + size(); } - - reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - - constexpr const_iterator cbegin() noexcept { return begin(); } - constexpr const_iterator cbegin() const noexcept { return begin(); } - constexpr const_iterator cend() noexcept { return end(); } - constexpr const_iterator cend() const noexcept { return end(); } - - ///@} // Iterators - -private: - /// \name Iterator bound-check utilites - ///@{ - - template - constexpr void assert_iterator_in_range(It it) noexcept { - static_assert(std::is_pointer_v); - SV_EXPECT(begin() <= it && "iterator not in range"); - SV_EXPECT(it <= end() && "iterator not in range"); - } - - template - constexpr void assert_valid_iterator_pair(It0 first, It1 last) noexcept { - static_assert(std::is_pointer_v); - static_assert(std::is_pointer_v); - SV_EXPECT(first <= last && "invalid iterator pair"); - } - - template - constexpr void assert_iterator_pair_in_range(It0 first, It1 last) noexcept { - assert_iterator_in_range(first); - assert_iterator_in_range(last); - assert_valid_iterator_pair(first, last); - } - - ///@} -public: - /// \name Element access - /// - ///@{ - - /// Unchecked access to element at index \p pos (UB if index not in - /// range) - constexpr reference operator[](size_type pos) noexcept { return std::begin(*this)[pos]; } - - /// Unchecked access to element at index \p pos (UB if index not in - /// range) - constexpr const_reference operator[](size_type pos) const noexcept { return std::begin(*this)[pos]; } - - /// Checked access to element at index \p pos (throws `out_of_range` - /// if index not in range) - constexpr reference at(size_type pos) noexcept { return std::begin(*this)[pos]; } - - /// Checked access to element at index \p pos (throws `out_of_range` - /// if index not in range) - constexpr const_reference at(size_type pos) const noexcept { return std::begin(*this)[pos]; } - - /// - constexpr reference front() noexcept { return std::begin(*this)[0]; } - constexpr const_reference front() const noexcept { return std::begin(*this)[0]; } - - constexpr reference back() noexcept { - SV_EXPECT(!empty() && "calling back on an empty vector"); - return std::begin(*this)[size() - 1]; - } - constexpr const_reference back() const noexcept { - SV_EXPECT(!empty() && "calling back on an empty vector"); - return std::begin(*this)[size() - 1]; - } - - ///@} // Element access - - /// \name Modifiers - ///@{ - - using base_t::emplace_back; - using base_t::pop_back; - - /// Clears the vector. - constexpr void clear() noexcept { - unsafe_destroy_all(); - unsafe_set_size(0); - } - - /// Appends \p value at the end of the vector. - template - requires std::constructible_from && std::assignable_from - constexpr void push_back(U&& value) noexcept(noexcept(emplace_back(std::forward(value)))) { - SV_EXPECT(!full() && "vector is full!"); - emplace_back(std::forward(value)); - } - - /// Appends a default constructed `T` at the end of the vector. - - void push_back() noexcept(noexcept(emplace_back(T{}))) - requires std::constructible_from && std::assignable_from - { - SV_EXPECT(!full() && "vector is full!"); - emplace_back(T{}); - } - - template - requires std::constructible_from - constexpr iterator emplace(const_iterator position, - Args&&... args) noexcept(noexcept(move_insert(position, std::declval(), - std::declval()))) { - SV_EXPECT(!full() && "tried emplace on full static_vector!"); - assert_iterator_in_range(position); - value_type a(std::forward(args)...); - return move_insert(position, &a, &a + 1); - } - - constexpr iterator insert(const_iterator position, - const_reference x) noexcept(noexcept(insert(position, size_type(1), x))) - requires std::copy_constructible - { - SV_EXPECT(!full() && "tried insert on full static_vector!"); - assert_iterator_in_range(position); - return insert(position, size_type(1), x); - } - - constexpr iterator insert(const_iterator position, - value_type&& x) noexcept(noexcept(move_insert(position, &x, &x + 1))) - requires std::move_constructible - { - SV_EXPECT(!full() && "tried insert on full static_vector!"); - assert_iterator_in_range(position); - return move_insert(position, &x, &x + 1); - } - - constexpr iterator insert(const_iterator position, size_type n, const T& x) noexcept(noexcept(push_back(x))) - requires std::copy_constructible - - { - assert_iterator_in_range(position); - const auto new_size = size() + n; - SV_EXPECT(new_size <= capacity() && "trying to insert beyond capacity!"); - auto b = end(); - while (n != 0) { - push_back(x); - --n; - } - - auto writable_position = begin() + (position - begin()); - std::rotate(writable_position, b, end()); - return writable_position; - } - - template - requires std::input_iterator - && std::constructible_from> - constexpr iterator insert(const_iterator position, InputIt first, - InputIt last) noexcept(noexcept(emplace_back(*first))) { - assert_iterator_in_range(position); - assert_valid_iterator_pair(first, last); - if constexpr (std::random_access_iterator) { - SV_EXPECT(size() + static_cast(last - first) <= capacity() - && "trying to insert beyond capacity!"); - } - auto b = end(); - - // insert at the end and then just rotate: - // cannot use try in constexpr function - // try { // if copy_constructor throws you get basic-guarantee? - for (; first != last; ++first) { - emplace_back(*first); - } - // } catch (...) { - // erase(b, end()); - // throw; - // } - - auto writable_position = begin() + (position - begin()); - std::rotate(writable_position, b, end()); - return writable_position; - } - - template - constexpr iterator move_insert(const_iterator position, InputIt first, - InputIt last) noexcept(noexcept(emplace_back(std::move(*first)))) { - assert_iterator_in_range(position); - assert_valid_iterator_pair(first, last); - if constexpr (std::random_access_iterator) { - SV_EXPECT(size() + static_cast(last - first) <= capacity() - && "trying to insert beyond capacity!"); - } - iterator b = end(); - - // we insert at the end and then just rotate: - for (; first != last; ++first) { - emplace_back(std::move(*first)); - } - auto writable_position = begin() + (position - begin()); - std::rotate(writable_position, b, end()); - return writable_position; - } - - constexpr iterator insert(const_iterator position, - std::initializer_list il) noexcept(noexcept(insert(position, il.begin(), il.end()))) - requires std::copy_constructible - { - assert_iterator_in_range(position); - return insert(position, il.begin(), il.end()); - } - - constexpr iterator erase(const_iterator position) noexcept - requires std::movable - { - assert_iterator_in_range(position); - return erase(position, position + 1); - } - - constexpr iterator erase(const_iterator first, const_iterator last) noexcept - requires std::movable - { - assert_iterator_pair_in_range(first, last); - iterator p = begin() + (first - begin()); - if (first != last) { - unsafe_destroy(std::move(p + (last - first), end(), p), end()); - unsafe_set_size(size() - static_cast(last - first)); - } - - return p; - } - - constexpr void swap(static_vector& other) noexcept(std::is_nothrow_swappable_v) - requires std::assignable_from - { - static_vector tmp = move(other); - other = move(*this); - (*this) = move(tmp); - } - - /// Resizes the container to contain \p sz elements. If elements - /// need to be appended, these are copy-constructed from \p value. - /// - constexpr void resize(size_type sz, T const& value) noexcept(std::is_nothrow_copy_constructible_v) - requires std::copy_constructible - { - if (sz == size()) { - return; - } - if (sz > size()) { - SV_EXPECT(sz <= capacity() - && "static_vector cannot be resized to " - "a size greater than capacity"); - insert(end(), sz - size(), value); - } - else { - erase(end() - (size() - sz), end()); - } - } - -private: - constexpr void - emplace_n(size_type n) noexcept((std::move_constructible && std::is_nothrow_move_constructible_v) - || (std::copy_constructible && std::is_nothrow_copy_constructible_v)) - requires std::move_constructible || std::copy_constructible - { - SV_EXPECT(n <= capacity() - && "static_vector cannot be " - "resized to a size greater than " - "capacity"); - while (n != size()) { - emplace_back(T{}); - } - } - -public: - /// Resizes the container to contain \p sz elements. If elements - /// need to be appended, these are move-constructed from `T{}` (or - /// copy-constructed if `T` is not `std::move_constructible`). - constexpr void - resize(size_type sz) noexcept((std::move_constructible && std::is_nothrow_move_constructible_v) - || (std::copy_constructible && std::is_nothrow_copy_constructible_v)) - requires std::movable - { - if (sz == size()) { - return; - } - - if (sz > size()) { - emplace_n(sz); - } - else { - erase(end() - (size() - sz), end()); - } - } - - ///@} // Modifiers - - /// \name Construct/copy/move/destroy - ///@{ - - /// Default constructor. - constexpr static_vector() = default; - - /// Copy constructor. - constexpr static_vector(static_vector const& other) noexcept(noexcept(insert(begin(), other.begin(), other.end()))) - requires std::copy_constructible - { - // nothin to assert: size of other cannot exceed capacity - // because both vectors have the same type - insert(begin(), other.begin(), other.end()); - } - - /// Move constructor. - constexpr static_vector(static_vector&& other) noexcept(noexcept(move_insert(begin(), other.begin(), other.end()))) - requires std::move_constructible - { - // nothin to assert: size of other cannot exceed capacity - // because both vectors have the same type - move_insert(begin(), other.begin(), other.end()); - } - - /// Copy assignment. - constexpr static_vector& - operator=(static_vector const& other) noexcept(noexcept(clear()) - && noexcept(insert(begin(), other.begin(), other.end()))) - requires std::assignable_from - { - // nothin to assert: size of other cannot exceed capacity - // because both vectors have the same type - clear(); - insert(this->begin(), other.begin(), other.end()); - return *this; - } - - /// Move assignment. - constexpr static_vector& - operator=(static_vector&& other) noexcept(noexcept(clear()) - and noexcept(move_insert(begin(), other.begin(), other.end()))) - requires std::move_constructible - { - // nothin to assert: size of other cannot exceed capacity - // because both vectors have the same type - clear(); - move_insert(this->begin(), other.begin(), other.end()); - return *this; - } - - /// Initializes vector with \p n default-constructed elements. - explicit constexpr static_vector(size_type n) noexcept(noexcept(emplace_n(n))) - requires std::copy_constructible || std::move_constructible - { - SV_EXPECT(n <= capacity() && "size exceeds capacity"); - emplace_n(n); - } - - /// Initializes vector with \p n with \p value. - constexpr static_vector(size_type n, T const& value) noexcept(noexcept(insert(begin(), n, value))) - requires std::copy_constructible - { - SV_EXPECT(n <= capacity() && "size exceeds capacity"); - insert(begin(), n, value); - } - - /// Initialize vector from range [first, last). - template - constexpr static_vector(InputIt first, InputIt last) { - if constexpr (std::random_access_iterator) { - SV_EXPECT(last - first >= 0); - SV_EXPECT(static_cast(last - first) <= capacity() && "range size exceeds capacity"); - } - insert(begin(), first, last); - } - - template U> - constexpr static_vector(std::initializer_list il) noexcept(noexcept(base_t(std::move(il)))) - : base_t(std::move(il)) { // assert happens in base_t constructor - } - - constexpr static_vector(std::initializer_list il) noexcept(noexcept(base_t(std::move(il)))) - : base_t(std::move(il)) { // assert happens in base_t constructor - } - - template - constexpr void assign(InputIt first, - InputIt last) noexcept(noexcept(clear()) and noexcept(insert(begin(), first, last))) { - if constexpr (std::random_access_iterator) { - SV_EXPECT(last - first >= 0); - SV_EXPECT(static_cast(last - first) <= capacity() && "range size exceeds capacity"); - } - clear(); - insert(begin(), first, last); - } - - constexpr void assign(size_type n, const T& u) - requires std::copy_constructible - { - SV_EXPECT(n <= capacity() && "size exceeds capacity"); - clear(); - insert(begin(), n, u); - } - - constexpr void assign(std::initializer_list const& il) - requires std::copy_constructible - { - SV_EXPECT(il.size() <= capacity() && "initializer_list size exceeds capacity"); - clear(); - insert(this->begin(), il.begin(), il.end()); - } - - constexpr void assign(std::initializer_list&& il) - requires std::copy_constructible - { - SV_EXPECT(il.size() <= capacity() && "initializer_list size exceeds capacity"); - clear(); - insert(this->begin(), il.begin(), il.end()); - } - - ///@} // Construct/copy/move/destroy/assign -}; - -template -constexpr bool operator==(static_vector const& a, static_vector const& b) noexcept { - return a.size() == b.size() and std::equal(a.begin(), a.end(), b.begin(), b.end(), std::equal_to<>{}); -} - -template -constexpr bool operator<(static_vector const& a, static_vector const& b) noexcept { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), std::less<>{}); -} - -template -constexpr bool operator!=(static_vector const& a, static_vector const& b) noexcept { - return not(a == b); -} - -template -constexpr bool operator<=(static_vector const& a, static_vector const& b) noexcept { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), std::less_equal<>{}); -} - -template -constexpr bool operator>(static_vector const& a, static_vector const& b) noexcept { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), std::greater<>{}); -} - -template -constexpr bool operator>=(static_vector const& a, static_vector const& b) noexcept { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), std::greater_equal<>{}); -} - -namespace sv_detail { -template -constexpr static_vector, N> to_static_vector_impl(T (&a)[N], std::index_sequence) { - return {{a[I]...}}; -} - -template -constexpr static_vector, N> to_static_vector_impl(T (&&a)[N], std::index_sequence) { - return {{std::move(a[I])...}}; -} -} // namespace sv_detail - -template -constexpr static_vector, N> to_static_vector(T (&a)[N]) { - return sv_detail::to_static_vector_impl(a, std::make_index_sequence{}); -} - -template -constexpr static_vector, N> to_static_vector(T (&&a)[N]) { - return sv_detail::to_static_vector_impl(std::move(a), std::make_index_sequence{}); -} - -} // namespace deluge - -// undefine all the internal macros -#undef SV_ASSUME -#undef SV_ASSERT -#undef SV_EXPECT diff --git a/src/deluge/util/exceptions.h b/src/deluge/util/exceptions.h index 7783dcedbd..dd81193bc3 100644 --- a/src/deluge/util/exceptions.h +++ b/src/deluge/util/exceptions.h @@ -3,5 +3,6 @@ namespace deluge { enum class exception { BAD_ALLOC, + BAD_RELEASE, }; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0a1fca600c..350afbf746 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,7 @@ if (WIN32 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Gcc") # CPPUTEST_PLATFORM defaults to Gcc, which causes the test binaries to # segfault on Windows/Clang builds, since they use the stdlib from MSVC. set(CPPUTEST_PLATFORM VisualCpp) -endif() +endif () set(DELUGE_C_STANDARD 23) set(DELUGE_CXX_STANDARD 23) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 12f1bd7ed2..97b2170e0d 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -5,26 +5,33 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/cpputest/cpputest.git GIT_TAG eccbc2190c672e598c1dd2bf5e4295f5ba27aad1 ) +FetchContent_Declare( + etl + GIT_REPOSITORY https://github.com/ETLCPP/etl + GIT_TAG 20.39.4 +) + +FetchContent_MakeAvailable(etl) # Set this to ON if you want to have the CppUTest's internal tests in your # project as well. set(TESTS OFF CACHE BOOL "Switch off CppUTest Test build") if (WIN32) - # Memory leak detection is off by default on Windows, setting - # it here will cause a massive spam of redefinition warning during - # build. - # - # We use things like strncat, which Windows wants to warn about: silence those. - add_compile_definitions( - IN_UNIT_TESTS=1 - _CRT_SECURE_NO_WARNINGS=1 - ) -else() - add_compile_definitions( - CPPUTEST_MEM_LEAK_DETECTION_DISABLED - IN_UNIT_TESTS=1 - ) -endif() + # Memory leak detection is off by default on Windows, setting + # it here will cause a massive spam of redefinition warning during + # build. + # + # We use things like strncat, which Windows wants to warn about: silence those. + add_compile_definitions( + IN_UNIT_TESTS=1 + _CRT_SECURE_NO_WARNINGS=1 + ) +else () + add_compile_definitions( + CPPUTEST_MEM_LEAK_DETECTION_DISABLED + IN_UNIT_TESTS=1 + ) +endif () FetchContent_MakeAvailable(CppUTest) @@ -84,7 +91,7 @@ set_target_properties(UnitTests CXX_EXTENSIONS ON ) -target_link_libraries(UnitTests CppUTestExt) +target_link_libraries(UnitTests CppUTestExt etl::etl) # strchr is seemingly different in x86 target_compile_options(UnitTests PUBLIC