-
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
base: add/stl-like-containers
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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>>>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,357 @@ | ||
| #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" | ||
|
|
||
| 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; | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| constexpr reference operator[](const size_type pos) { | ||
| OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); | ||
| return _container[pos]; | ||
| } | ||
|
|
||
| constexpr const_reference operator[](const size_type pos) const { | ||
| OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); | ||
| return _container[pos]; | ||
| } | ||
|
|
||
| constexpr reference front() { | ||
| OV_HARDEN_ASSERT_NONEMPTY("front"); | ||
| return _container[0]; | ||
| } | ||
|
|
||
| constexpr const_reference front() const { | ||
| OV_HARDEN_ASSERT_NONEMPTY("front"); | ||
| return _container[0]; | ||
| } | ||
|
|
||
| constexpr reference back() { | ||
| OV_HARDEN_ASSERT_NONEMPTY("back"); | ||
| return _container[size() - 1]; | ||
| } | ||
|
|
||
| constexpr const_reference back() const { | ||
| OV_HARDEN_ASSERT_NONEMPTY("back"); | ||
| return _container[size() - 1]; | ||
| } | ||
|
|
||
| constexpr value_type* data() { | ||
| return _container.data(); | ||
| } | ||
|
|
||
| constexpr value_type const* data() const { | ||
| return _container.data(); | ||
| } | ||
|
|
||
| constexpr iterator begin() { | ||
| return _container.begin(); | ||
| } | ||
|
|
||
| constexpr const_iterator begin() const { | ||
| return _container.begin(); | ||
| } | ||
|
|
||
| constexpr const_iterator cbegin() const { | ||
| return _container.cbegin(); | ||
| } | ||
|
|
||
| constexpr iterator end() { | ||
| return _container.end(); | ||
| } | ||
|
|
||
| constexpr const_iterator end() const { | ||
| return _container.end(); | ||
| } | ||
|
|
||
| constexpr const_iterator cend() const { | ||
| return _container.cend(); | ||
| } | ||
|
|
||
| constexpr reverse_iterator rbegin() { | ||
| return _container.rbegin(); | ||
| } | ||
|
|
||
| constexpr const_reverse_iterator rbegin() const { | ||
| return _container.rbegin(); | ||
| } | ||
|
|
||
| constexpr const_reverse_iterator crbegin() const { | ||
| return _container.crbegin(); | ||
| } | ||
|
|
||
| constexpr reverse_iterator rend() { | ||
| return _container.rend(); | ||
| } | ||
|
|
||
| constexpr const_reverse_iterator rend() const { | ||
| return _container.rend(); | ||
| } | ||
|
|
||
| constexpr const_reverse_iterator crend() const { | ||
| return _container.crend(); | ||
| } | ||
|
|
||
| constexpr bool empty() const { | ||
| return _container.empty(); | ||
| } | ||
|
|
||
| constexpr size_type size() const { | ||
| return _container.size(); | ||
| } | ||
|
|
||
| constexpr size_type max_size() const { | ||
| return _container.max_size(); | ||
| } | ||
|
|
||
| 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) { | ||
| 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) { | ||
| if (count > capacity()) { | ||
| reserve_minimum(count); | ||
| } | ||
| _container.resize(count); | ||
| } | ||
|
|
||
| constexpr void reserve_minimum(size_type count) { | ||
| if (count > capacity()) { | ||
| _container.reserve(_get_capacity_for(count)); | ||
| } | ||
| } | ||
|
|
||
| constexpr void reserve_exact(size_type count) { | ||
| _container.reserve(count); | ||
| } | ||
|
|
||
| constexpr container_type const& container() const { | ||
| return _container; | ||
| } | ||
|
|
||
| constexpr container_type&& release() && { | ||
| return std::move(_container); | ||
| } | ||
|
|
||
| private: | ||
| container_type _container; | ||
|
|
||
| constexpr size_type _get_capacity_for(const size_type value) const { | ||
| const size_type max = max_size(); | ||
| if (max < value) { | ||
| return max; | ||
| } | ||
|
|
||
| if (value < capacity()) { | ||
| return value; | ||
| } | ||
|
|
||
| const size_type old_capacity = capacity(); | ||
| const size_type new_capacity = old_capacity * growth_trait::numerator / growth_trait::denominator; | ||
|
|
||
| if (new_capacity > max || | ||
| (old_capacity != 0 && new_capacity / growth_trait::numerator < old_capacity / growth_trait::denominator)) { | ||
| return max; | ||
| } | ||
|
|
||
| if (old_capacity == size_type {} && new_capacity < growth_trait::initial_allocation_size) { | ||
| return growth_trait::initial_allocation_size; | ||
| } | ||
|
|
||
| if (value > new_capacity) { | ||
| return value; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems wrong.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only specifically in the case that you reserve more than the next factor size, this is what all the standard libraries will do with resize. |
||
| } | ||
|
|
||
| return new_capacity; | ||
| } | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.