-
Notifications
You must be signed in to change notification settings - Fork 10
Add tunable_vector std::vector wrapper container
#746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Spartan322
wants to merge
1
commit into
master
Choose a base branch
from
add/tunable-vector
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+386
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| #pragma once | ||
|
|
||
| #include <foonathan/memory/default_allocator.hpp> | ||
| #include <foonathan/memory/std_allocator.hpp> | ||
|
|
||
| #include "openvic-simulation/core/memory/MemoryTracker.hpp" | ||
| #include "openvic-simulation/core/stl/containers/TunableVector.hpp" | ||
|
|
||
| namespace OpenVic::memory { | ||
| template< | ||
| typename T, ::OpenVic::stl::_detail::tunable_growth_trait GrowthTrait = ::OpenVic::stl::default_growth_traits, | ||
| class RawAllocator = foonathan::memory::default_allocator> | ||
| using tunable_vector = | ||
| ::OpenVic::stl::tunable_vector<T, GrowthTrait, foonathan::memory::std_allocator<T, tracker<RawAllocator>>>; | ||
| } |
371 changes: 371 additions & 0 deletions
371
src/openvic-simulation/core/stl/containers/TunableVector.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,371 @@ | ||
| #pragma once | ||
|
|
||
| #include <algorithm> | ||
| #include <concepts> | ||
| #include <cstddef> | ||
| #include <initializer_list> | ||
| #include <iterator> | ||
| #include <memory> | ||
| #include <ranges> | ||
| #include <type_traits> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include <range/v3/algorithm/move.hpp> | ||
|
|
||
| #include "openvic-simulation/core/Assert.hpp" | ||
| #include "openvic-simulation/core/Typedefs.hpp" | ||
|
|
||
| namespace OpenVic::stl { | ||
| template<size_t Num, size_t Denom, size_t InitAllocSize = 0> | ||
| struct growth_traits { | ||
| static constexpr std::integral_constant<size_t, Num> numerator {}; | ||
| static constexpr std::integral_constant<size_t, Denom> denominator {}; | ||
| static constexpr std::integral_constant<size_t, InitAllocSize> initial_allocation_size {}; | ||
| }; | ||
|
|
||
| // Uses approximated golden ratio (1.617...) | ||
| using default_growth_traits = growth_traits<55, 34>; | ||
|
|
||
| namespace _detail { | ||
| template<typename T> | ||
| concept tunable_growth_trait = requires() { | ||
| { T::numerator() } -> std::convertible_to<size_t>; | ||
| { T::denominator() } -> std::convertible_to<size_t>; | ||
| { T::initial_allocation_size() } -> std::convertible_to<size_t>; | ||
| }; | ||
|
|
||
| template<typename Range, typename T> | ||
| concept _compatible_range = | ||
| std::ranges::input_range<Range> && std::convertible_to<std::ranges::range_reference_t<Range>, T>; | ||
| } | ||
|
|
||
| template< | ||
| typename T, _detail::tunable_growth_trait GrowthTrait = default_growth_traits, typename Allocator = std::allocator<T>> | ||
| class tunable_vector { | ||
| public: | ||
| using value_type = T; | ||
| using allocator_type = Allocator; | ||
| using container_type = std::vector<value_type, allocator_type>; | ||
| using size_type = typename container_type::size_type; | ||
| using difference_type = typename container_type::difference_type; | ||
| using reference = typename container_type::reference; | ||
| using const_reference = typename container_type::const_reference; | ||
| using pointer = typename container_type::pointer; | ||
| using const_pointer = typename container_type::const_pointer; | ||
| using iterator = typename container_type::iterator; | ||
| using const_iterator = typename container_type::const_iterator; | ||
| using reverse_iterator = typename container_type::reverse_iterator; | ||
| using const_reverse_iterator = typename container_type::const_reverse_iterator; | ||
| using growth_trait = GrowthTrait; | ||
|
|
||
| static_assert(growth_trait::denominator() != 0, "growth_trait's denominator cannot be 0"); | ||
| static_assert(growth_trait::numerator() > growth_trait::denominator(), "growth_trait's divisor must be more than 1"); | ||
|
|
||
| constexpr tunable_vector() : _container() {} | ||
| explicit constexpr tunable_vector(allocator_type const& alloc) : _container(alloc) {} | ||
| explicit constexpr tunable_vector(size_type count, allocator_type const& alloc = allocator_type {}) | ||
| : _container(count, alloc) {} | ||
| constexpr tunable_vector(size_type count, value_type const& value, allocator_type const& alloc = allocator_type {}) | ||
| : _container(count, value, alloc) {} | ||
| template<typename InputIt> | ||
| constexpr tunable_vector(InputIt first, InputIt last, allocator_type const& alloc = allocator_type {}) | ||
| : _container(first, last, alloc) {} | ||
|
|
||
| constexpr tunable_vector(container_type const& other) : _container { other } {} | ||
| constexpr tunable_vector(container_type&& other) : _container { std::move(other) } {} | ||
|
|
||
| constexpr tunable_vector(container_type const& other, allocator_type const& alloc) : _container { other, alloc } {} | ||
| constexpr tunable_vector(container_type&& other, allocator_type const& alloc) | ||
| : _container { std::move(other), alloc } {} | ||
|
|
||
| constexpr tunable_vector(tunable_vector const& other) : _container { other._container } {} | ||
| constexpr tunable_vector(tunable_vector&& other) : _container { std::move(other._container) } {} | ||
|
|
||
| constexpr tunable_vector(tunable_vector const& other, allocator_type const& alloc) | ||
| : _container { other._container, alloc } {} | ||
| constexpr tunable_vector(tunable_vector&& other, allocator_type const& alloc) | ||
| : _container { std::move(other._container), alloc } {} | ||
|
|
||
| template<typename OtherGrowth> | ||
| constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator> const& other) : _container { other._container } {} | ||
| template<typename OtherGrowth> | ||
| constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator>&& other) | ||
| : _container { std::move(other._container) } {} | ||
|
|
||
| template<typename OtherGrowth> | ||
| constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator> const& other, allocator_type const& alloc) | ||
| : _container { other._container, alloc } {} | ||
| template<typename OtherGrowth> | ||
| constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator>&& other, allocator_type const& alloc) | ||
| : _container { std::move(other._container), alloc } {} | ||
|
|
||
| constexpr tunable_vector(std::initializer_list<value_type> init, allocator_type const& alloc = allocator_type {}) | ||
| : _container(init, alloc) {} | ||
|
|
||
| constexpr tunable_vector& operator=(container_type const& other) { | ||
| _container = other; | ||
| return *this; | ||
| } | ||
| constexpr tunable_vector& operator=(container_type&& other) { | ||
| _container = std::move(other); | ||
| return *this; | ||
| } | ||
|
|
||
| constexpr tunable_vector& operator=(tunable_vector const& other) { | ||
| _container = other._container; | ||
| return *this; | ||
| } | ||
| constexpr tunable_vector& operator=(tunable_vector&& other) { | ||
| _container = std::move(other._container); | ||
| return *this; | ||
| } | ||
|
|
||
| tunable_vector& operator=(std::initializer_list<value_type> ilist) { | ||
| _container = ilist; | ||
| return *this; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr reference operator[](const size_type pos) { | ||
| OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); | ||
| return _container[pos]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reference operator[](const size_type pos) const { | ||
| OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); | ||
| return _container[pos]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr reference front() { | ||
| OV_HARDEN_ASSERT_NONEMPTY("front"); | ||
| return _container[0]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reference front() const { | ||
| OV_HARDEN_ASSERT_NONEMPTY("front"); | ||
| return _container[0]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr reference back() { | ||
| OV_HARDEN_ASSERT_NONEMPTY("back"); | ||
| return _container[size() - 1]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reference back() const { | ||
| OV_HARDEN_ASSERT_NONEMPTY("back"); | ||
| return _container[size() - 1]; | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr value_type* data() { | ||
| return _container.data(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr value_type const* data() const { | ||
| return _container.data(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr iterator begin() { | ||
| return _container.begin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_iterator begin() const { | ||
| return _container.begin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_iterator cbegin() const { | ||
| return _container.cbegin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr iterator end() { | ||
| return _container.end(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_iterator end() const { | ||
| return _container.end(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_iterator cend() const { | ||
| return _container.cend(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr reverse_iterator rbegin() { | ||
| return _container.rbegin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reverse_iterator rbegin() const { | ||
| return _container.rbegin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reverse_iterator crbegin() const { | ||
| return _container.crbegin(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr reverse_iterator rend() { | ||
| return _container.rend(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr const_reverse_iterator rend() const { | ||
| return _container.rend(); | ||
| } | ||
|
|
||
| constexpr const_reverse_iterator crend() const { | ||
| return _container.crend(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr bool empty() const { | ||
| return _container.empty(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr size_type size() const { | ||
| return _container.size(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr size_type max_size() const { | ||
| return _container.max_size(); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr size_type capacity() const { | ||
| return _container.capacity(); | ||
| } | ||
|
|
||
| constexpr void shrink_to_fit() { | ||
| _container.shrink_to_fit(); | ||
| } | ||
|
|
||
| constexpr void clear() { | ||
| _container.clear(); | ||
| } | ||
|
|
||
| constexpr iterator erase(const_iterator pos) { | ||
| return _container.erase(pos); | ||
| } | ||
|
|
||
| constexpr iterator erase(const_iterator first, const_iterator last) { | ||
| return _container.erase(first, last); | ||
| } | ||
|
|
||
| constexpr void swap(tunable_vector& vector) { | ||
| _container.swap(vector._container); | ||
| } | ||
|
|
||
| constexpr allocator_type get_allocator() const { | ||
| return _container.get_allocator(); | ||
| } | ||
|
|
||
| constexpr void push_back(value_type const& value) { | ||
| if (size() == capacity()) { | ||
| reserve_minimum(capacity() + 1); | ||
| } | ||
| _container.push_back(value); | ||
| } | ||
|
|
||
| constexpr void push_back(value_type&& value) { | ||
| if (size() == capacity()) { | ||
| reserve_minimum(capacity() + 1); | ||
| } | ||
| _container.push_back(std::move(value)); | ||
| } | ||
|
|
||
| template<typename... Args> | ||
| constexpr reference emplace_back(Args&&... args) { | ||
| if (size() == capacity()) { | ||
| reserve_minimum(capacity() + 1); | ||
| } | ||
| return _container.emplace_back(std::forward<Args>(args)...); | ||
| } | ||
|
|
||
| template<_detail::_compatible_range<T> RangeT> | ||
| constexpr void append_range(RangeT&& range) { | ||
|
wvpm marked this conversation as resolved.
|
||
| if constexpr (std::ranges::forward_range<RangeT> || std::ranges::sized_range<RangeT>) { | ||
| reserve_minimum(std::ranges::distance(range)); | ||
| ranges::move(range, std::back_inserter(*this)); | ||
| } else { | ||
| auto first = std::ranges::begin(range); | ||
| const auto last = std::ranges::end(range); | ||
|
|
||
| for (size_type free = capacity() - size(); first != last && free != size_type {}; | ||
| std::ranges::advance(first, 1), --free) { | ||
| emplace_back(*first); | ||
| } | ||
|
|
||
| if (first == last) { | ||
| return; | ||
| } | ||
|
|
||
| tunable_vector<value_type> tmp { get_allocator() }; | ||
| for (; first != last; std::ranges::advance(first, 1)) { | ||
| tmp.emplace_back(*first); | ||
| } | ||
| std::ranges::subrange subrange(std::make_move_iterator(tmp.begin()), std::make_move_iterator(tmp.end())); | ||
| append_range(subrange); | ||
| } | ||
| } | ||
|
|
||
| constexpr void resize(size_type count) { | ||
| reserve_minimum(count); | ||
| _container.resize(count); | ||
| } | ||
|
|
||
| // Prefer reserve_minimum to be more explicit and obvious. | ||
| // This is only for making it a drop-in replacement of vector. | ||
| constexpr void reserve(size_type count) { | ||
| reserve_minimum(count); | ||
| } | ||
|
|
||
| constexpr void reserve_minimum(size_type count) { | ||
|
wvpm marked this conversation as resolved.
|
||
| if (count > capacity()) { | ||
| _container.reserve(_get_capacity_for(count)); | ||
| } | ||
| } | ||
|
|
||
| constexpr void reserve_exact(size_type count) { | ||
| _container.reserve(count); | ||
| } | ||
|
|
||
| [[nodiscard]] constexpr container_type const& container() const { | ||
| return _container; | ||
| } | ||
|
|
||
| constexpr container_type&& release() && { | ||
| return std::move(_container); | ||
| } | ||
|
|
||
| private: | ||
| container_type _container; | ||
|
|
||
| [[nodiscard]] constexpr size_type _get_capacity_for(const size_type value) const { | ||
| const size_type max = max_size(); | ||
| const size_type old_capacity = capacity(); | ||
|
|
||
| if (max <= value) { | ||
| return max; | ||
| } | ||
|
|
||
| if (value <= old_capacity) { | ||
| return value; | ||
| } | ||
|
|
||
| const size_type new_capacity = old_capacity * growth_trait::numerator() / growth_trait::denominator(); | ||
|
|
||
| // If true, new_capacity overflowed something | ||
| if (OV_unlikely(new_capacity == size_type {} || new_capacity < old_capacity || new_capacity > max)) { | ||
| return max; | ||
| } | ||
|
|
||
| if (new_capacity < growth_trait::initial_allocation_size()) { | ||
| return growth_trait::initial_allocation_size(); | ||
| } | ||
|
|
||
| // Something odd has happened | ||
| if (unlikely(new_capacity / growth_trait::numerator() < old_capacity / growth_trait::denominator())) { | ||
| return value; | ||
| } | ||
|
|
||
| if (value > new_capacity) { | ||
| return value; | ||
|
wvpm marked this conversation as resolved.
|
||
| } | ||
|
|
||
| return new_capacity; | ||
| } | ||
| }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/OpenVicProject/OpenVic-Simulation/pull/754/changes#diff-66b217c20a8a9c4dcc694b0c9515140cafa6b47f01a4aaa7127fba1c129ef66c
Does the same. This is a common concern.
We can leave it here for now and replace it with RangeConcepts.hpp once that's merged.
Alternatively, feel free to extract it now in case we need it for other PRs as well.