From c99cd05d0749e9f84af6b33b3feebfadfe089b28 Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Wed, 20 May 2026 02:04:39 -0400 Subject: [PATCH 1/2] Move STL-like container headers to `core/stl/containers` Includes: - CowPtr - CowVector - FixedVector - RingBuffer - StackString - TypedSpan Move allocator aware STL-like containers to `OpenVic::stl` namespace Includes: - CowPtr - CowVector - FixedVector - RingBuffer Move BasicIterator to `core/stl` Move TslHelper.hpp to `core/stl` and rename to MutableIterator.hpp Move memory container type aliases to `core/memory` Add memory type alias for cow_ptr Add memory type alias for cow_vector Move FixedVector from namespace `OpenVic::_detail` to `OpenVic::stl` Add stateful allocator support to FixedVector's constructors Replace assert in TypedSpan with `OV_HARDEN_ASSERT_ACCESS` Move STL-like container unit tests to `core/stl/containers` --- .../economy/production/ArtisanalProducer.hpp | 2 +- .../economy/production/ProductionType.hpp | 4 ++-- .../economy/production/ResourceGatheringOperation.hpp | 2 +- src/openvic-simulation/economy/trading/GoodMarket.hpp | 2 +- .../modifier/ModifierEffectCache.hpp | 4 ++-- src/openvic-simulation/population/Pop.cpp | 10 +++++----- .../population/PopValuesFromProvince.hpp | 2 +- src/openvic-simulation/utility/ThreadPool.cpp | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp index fe7f75dc..df652c5e 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp @@ -89,7 +89,7 @@ namespace OpenVic { ProvinceInstance& location, const fixed_point_t max_cost_multiplier ); - + private: struct artisan_tick_handler { private: diff --git a/src/openvic-simulation/economy/production/ProductionType.hpp b/src/openvic-simulation/economy/production/ProductionType.hpp index 361dd0fa..bfaeeb7d 100644 --- a/src/openvic-simulation/economy/production/ProductionType.hpp +++ b/src/openvic-simulation/economy/production/ProductionType.hpp @@ -23,7 +23,7 @@ namespace OpenVic { const effect_t effect_type; const fixed_point_t effect_multiplier; const fixed_point_t amount; - + constexpr Job( const pop_type_index_t new_pop_type_index, const effect_t new_effect_type, @@ -150,4 +150,4 @@ namespace OpenVic { bool parse_scripts(DefinitionManager const& definition_manager); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp index 985a618f..04749197 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp @@ -73,4 +73,4 @@ namespace OpenVic { static constexpr size_t VECTORS_FOR_RGO_TICK = 1; void rgo_tick(memory::vector& reusable_vector); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/economy/trading/GoodMarket.hpp b/src/openvic-simulation/economy/trading/GoodMarket.hpp index f09b8328..6121802c 100644 --- a/src/openvic-simulation/economy/trading/GoodMarket.hpp +++ b/src/openvic-simulation/economy/trading/GoodMarket.hpp @@ -77,4 +77,4 @@ namespace OpenVic { void on_use_exponential_price_changes_changed(); void record_price_history(); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/modifier/ModifierEffectCache.hpp b/src/openvic-simulation/modifier/ModifierEffectCache.hpp index c055cf95..8cd9b186 100644 --- a/src/openvic-simulation/modifier/ModifierEffectCache.hpp +++ b/src/openvic-simulation/modifier/ModifierEffectCache.hpp @@ -339,7 +339,7 @@ namespace OpenVic { constexpr strata_effects_t() {}; }; - + building_type_effects_t const& get_building_type_effects(BuildingType const& key) const; good_effects_t const& get_good_effects(GoodDefinition const& key) const; regiment_type_effects_t const& get_regiment_type_effects(RegimentType const& key) const; @@ -367,4 +367,4 @@ namespace OpenVic { research_bonus_effects { create_empty } {} }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/population/Pop.cpp b/src/openvic-simulation/population/Pop.cpp index f3c7f461..cc55d866 100644 --- a/src/openvic-simulation/population/Pop.cpp +++ b/src/openvic-simulation/population/Pop.cpp @@ -139,10 +139,10 @@ void Pop::setup_pop_test_values(TypedSpan reforms) /* All entries equally weighted for testing. */ fill_span_with_test_weights(supporter_equivalents_by_ideology, 1, 5); rescale_span(supporter_equivalents_by_ideology, type_safe::get(size)); - + fill_span_with_test_weights(supporter_equivalents_by_party_policy, 3, 6); rescale_span(supporter_equivalents_by_party_policy, type_safe::get(size)); - + fill_span_with_test_weights(supporter_equivalents_by_reform, 3, 6); for (Reform const& reform : reforms) { if (reform.group.is_civilizing()) { @@ -498,7 +498,7 @@ void Pop::pop_tick_without_cleanup( money_to_spend_per_good.resize(good_count, 0); cash_allocated_for_artisanal_spending = 0; fill_needs_fulfilled_goods_with_false(); - + fixed_point_map_t goods_to_sell {}; if (artisanal_producer_optional.has_value()) { //execute artisan_tick before needs @@ -611,7 +611,7 @@ void Pop::pop_tick_without_cleanup( if (max_quantity_to_buy <= 0) { continue; } - + const fixed_point_t money_to_spend = money_to_spend_per_good[i]; market_instance.place_buy_up_to_order({ @@ -685,7 +685,7 @@ void Pop::after_buy(void* actor, BuyResult const& buy_result) { } CountryInstance* get_country_to_report_economy_nullable = pop.get_location().get_country_to_report_economy(); - + #define CONSUME_NEED(need_category) \ if (quantity_left_to_consume <= 0) { \ return; \ diff --git a/src/openvic-simulation/population/PopValuesFromProvince.hpp b/src/openvic-simulation/population/PopValuesFromProvince.hpp index 60820c63..3dacc799 100644 --- a/src/openvic-simulation/population/PopValuesFromProvince.hpp +++ b/src/openvic-simulation/population/PopValuesFromProvince.hpp @@ -58,4 +58,4 @@ namespace OpenVic { void update_pop_values_from_province(ProvinceInstance& province); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/utility/ThreadPool.cpp b/src/openvic-simulation/utility/ThreadPool.cpp index 77c02f9b..d66e56d6 100644 --- a/src/openvic-simulation/utility/ThreadPool.cpp +++ b/src/openvic-simulation/utility/ThreadPool.cpp @@ -246,7 +246,7 @@ void ThreadPool::initialise_threadpool( threads.reserve(max_worker_threads); work_per_thread.resize(max_worker_threads, work_t::NONE); - + const auto [work_bundles_quotient, work_bundles_remainder] = std::ldiv(WORK_BUNDLE_COUNT, max_worker_threads); auto work_bundles_begin = all_work_bundles.begin(); @@ -309,4 +309,4 @@ void ThreadPool::process_country_ticks_before_map() { void ThreadPool::process_country_ticks_after_map(){ process_work(work_t::COUNTRY_TICK_AFTER_MAP); -} \ No newline at end of file +} From 4e5d02738849595030ac0367d43ef4ad04a8756e Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Wed, 20 May 2026 12:26:13 -0400 Subject: [PATCH 2/2] Add tunable_vector `std::vector` wrapper container Add tunable_vector memory alias to `core/memory` --- .../core/memory/TunableVector.hpp | 15 + .../core/stl/containers/TunableVector.hpp | 357 ++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 src/openvic-simulation/core/memory/TunableVector.hpp create mode 100644 src/openvic-simulation/core/stl/containers/TunableVector.hpp diff --git a/src/openvic-simulation/core/memory/TunableVector.hpp b/src/openvic-simulation/core/memory/TunableVector.hpp new file mode 100644 index 00000000..66ead827 --- /dev/null +++ b/src/openvic-simulation/core/memory/TunableVector.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#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>>; +} diff --git a/src/openvic-simulation/core/stl/containers/TunableVector.hpp b/src/openvic-simulation/core/stl/containers/TunableVector.hpp new file mode 100644 index 00000000..78c1586e --- /dev/null +++ b/src/openvic-simulation/core/stl/containers/TunableVector.hpp @@ -0,0 +1,357 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "openvic-simulation/core/Assert.hpp" + +namespace OpenVic::stl { + template + struct growth_traits { + static constexpr std::integral_constant numerator {}; + static constexpr std::integral_constant denominator {}; + static constexpr std::integral_constant initial_allocation_size {}; + }; + + // Uses approximated golden ratio (1.617...) + using default_growth_traits = growth_traits<55, 34>; + + namespace _detail { + template + concept tunable_growth_trait = requires() { + { T::numerator } -> std::convertible_to; + { T::denominator } -> std::convertible_to; + { T::initial_allocation_size } -> std::convertible_to; + }; + + template + concept _compatible_range = + std::ranges::input_range && std::convertible_to, T>; + } + + template< + typename T, _detail::tunable_growth_trait GrowthTrait = default_growth_traits, typename Allocator = std::allocator> + class tunable_vector { + public: + using value_type = T; + using allocator_type = Allocator; + using container_type = std::vector; + 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 + 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 + constexpr tunable_vector(tunable_vector const& other) : _container { other._container } {} + template + constexpr tunable_vector(tunable_vector&& other) + : _container { std::move(other._container) } {} + + template + constexpr tunable_vector(tunable_vector const& other, allocator_type const& alloc) + : _container { other._container, alloc } {} + template + constexpr tunable_vector(tunable_vector&& other, allocator_type const& alloc) + : _container { std::move(other._container), alloc } {} + + constexpr tunable_vector(std::initializer_list 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 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 + constexpr reference emplace_back(Args&&... args) { + if (size() == capacity()) { + reserve_minimum(capacity() + 1); + } + return _container.emplace_back(std::forward(args)...); + } + + template<_detail::_compatible_range RangeT> + constexpr void append_range(RangeT&& range) { + if constexpr (std::ranges::forward_range || std::ranges::sized_range) { + 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 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; + } + + return new_capacity; + } + }; +}