Skip to content

Commit ba1121f

Browse files
committed
Add bounded_vector
1 parent a82e9a6 commit ba1121f

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ target_sources(containers PUBLIC
269269
source/containers/begin_end.cpp
270270
source/containers/bidirectional_linked_list.cpp
271271
source/containers/bidirectional_range.cpp
272+
source/containers/bounded_vector.cpp
272273
source/containers/c_array.cpp
273274
source/containers/can_set_size.cpp
274275
source/containers/clear.cpp
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright David Stone 2018.
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
module;
7+
8+
#include <bounded/assert.hpp>
9+
10+
#include <operators/bracket.hpp>
11+
#include <operators/forward.hpp>
12+
13+
export module containers.bounded_vector;
14+
15+
import containers.algorithms.destroy_range;
16+
import containers.algorithms.uninitialized;
17+
import containers.assign;
18+
import containers.assign_to_empty;
19+
import containers.begin_end;
20+
import containers.c_array;
21+
import containers.common_functions;
22+
import containers.compare_container;
23+
import containers.get_source_size;
24+
import containers.initializer_range;
25+
import containers.maximum_array_size;
26+
import containers.reserve_space_for;
27+
import containers.size_then_use_range;
28+
import containers.uninitialized_dynamic_array;
29+
30+
import bounded;
31+
import std_module;
32+
33+
using namespace bounded::literal;
34+
35+
namespace containers {
36+
37+
// Cannot use `array_size_type<T>` because that would not support incomplete
38+
// types.
39+
export template<typename T, array_size_type<std::byte> min_capacity, array_size_type<std::byte> max_capacity>
40+
struct [[clang::trivial_abi]] bounded_vector : private lexicographical_comparison::base {
41+
template<typename U, array_size_type<std::byte> other_min_capacity, array_size_type<std::byte> other_max_capacity>
42+
friend struct bounded_vector;
43+
44+
using size_type = bounded::integer<0, bounded::normalize<max_capacity>>;
45+
46+
template<typename Capacity>
47+
constexpr bounded_vector(reserve_space_for<Capacity> const capacity_):
48+
m_storage(capacity_.value),
49+
m_size(0_bi)
50+
{
51+
}
52+
53+
constexpr bounded_vector() noexcept(min_capacity == 0_bi):
54+
m_storage(bounded::constant<min_capacity>),
55+
m_size(0_bi)
56+
{
57+
}
58+
59+
template<constructor_initializer_range<bounded_vector> Source>
60+
constexpr explicit bounded_vector(Source && source):
61+
m_storage(bounded::constant<min_capacity>),
62+
m_size(0_bi)
63+
{
64+
::containers::assign_to_empty(*this, OPERATORS_FORWARD(source));
65+
}
66+
67+
template<constructor_initializer_range<bounded_vector> Source> requires size_then_use_range<Source>
68+
constexpr explicit bounded_vector(Source && source):
69+
bounded_vector(
70+
OPERATORS_FORWARD(source),
71+
::containers::get_source_size<bounded_vector>(source)
72+
)
73+
{
74+
}
75+
76+
template<std::size_t source_size> requires(source_size <= max_capacity)
77+
constexpr bounded_vector(c_array<T, source_size> && source):
78+
bounded_vector(std::move(source), bounded::constant<source_size>)
79+
{
80+
}
81+
template<std::same_as<empty_c_array_parameter> Source = empty_c_array_parameter>
82+
constexpr bounded_vector(Source):
83+
bounded_vector()
84+
{
85+
}
86+
87+
// If `other.capacity()` is outside [min_capacity, max_capacity], the
88+
// behavior is undefined.
89+
// TODO: throw exception instead
90+
template<array_size_type<std::byte> other_min_capacity, array_size_type<std::byte> other_max_capacity>
91+
constexpr explicit bounded_vector(bounded_vector<T, other_min_capacity, other_max_capacity> && other) noexcept:
92+
m_storage(std::move(other.m_storage)),
93+
m_size(bounded::assume_in_range<size_type>(std::exchange(other.m_size, 0_bi)))
94+
{
95+
}
96+
// TODO: Support trivial relocatability
97+
constexpr bounded_vector(bounded_vector && other) noexcept:
98+
m_storage(std::move(other.m_storage)),
99+
m_size(std::exchange(other.m_size, 0_bi))
100+
{
101+
}
102+
103+
constexpr bounded_vector(bounded_vector const & other):
104+
m_storage(reservation_size(other.size())),
105+
m_size(other.size())
106+
{
107+
::containers::uninitialized_copy_no_overlap(OPERATORS_FORWARD(other), data());
108+
}
109+
110+
constexpr ~bounded_vector() {
111+
::containers::destroy_range(*this);
112+
}
113+
114+
constexpr auto operator=(bounded_vector && other) & noexcept -> bounded_vector & {
115+
::containers::destroy_range(*this);
116+
m_size = other.m_size;
117+
other.m_size = 0_bi;
118+
m_storage = std::move(other.m_storage);
119+
return *this;
120+
}
121+
constexpr auto operator=(bounded_vector const & other) & -> bounded_vector & {
122+
if (this == std::addressof(other)) {
123+
return *this;
124+
}
125+
if constexpr (min_capacity > 0_bi) {
126+
if (!m_storage.data()) {
127+
BOUNDED_ASSERT(m_size == 0_bi);
128+
m_storage.replace_allocation(reservation_size(other.size()));
129+
::containers::uninitialized_copy_no_overlap(other, data());
130+
m_size = other.m_size;
131+
return *this;
132+
}
133+
}
134+
containers::assign(*this, other);
135+
return *this;
136+
}
137+
138+
friend constexpr auto swap(bounded_vector & lhs, bounded_vector & rhs) noexcept -> void {
139+
swap(lhs.m_storage, rhs.m_storage);
140+
std::swap(lhs.m_size, rhs.m_size);
141+
}
142+
143+
constexpr auto data() const -> T const * {
144+
return m_storage.data();
145+
}
146+
constexpr auto data() -> T * {
147+
return m_storage.data();
148+
}
149+
constexpr auto size() const -> size_type {
150+
return m_size;
151+
}
152+
153+
OPERATORS_BRACKET_SEQUENCE_RANGE_DEFINITIONS
154+
155+
static constexpr auto capacity() requires(min_capacity == max_capacity) {
156+
return bounded::constant<min_capacity>;
157+
}
158+
constexpr auto capacity() const requires(min_capacity != max_capacity) {
159+
return m_storage.capacity();
160+
}
161+
// Assumes that elements are already constructed in the spare capacity
162+
constexpr auto set_size(auto const new_size) -> void {
163+
BOUNDED_ASSERT(new_size <= capacity());
164+
m_size = new_size;
165+
}
166+
167+
constexpr auto replace_empty_allocation(size_type const requested_capacity) -> void {
168+
BOUNDED_ASSERT(size() == 0_bi);
169+
m_storage.replace_allocation(reservation_size(requested_capacity));
170+
}
171+
constexpr auto reserve(size_type const requested_capacity) -> void {
172+
if (requested_capacity <= capacity()) {
173+
return;
174+
}
175+
auto temp = storage_type(reservation_size(requested_capacity));
176+
containers::uninitialized_relocate_no_overlap(
177+
*this,
178+
temp.data()
179+
);
180+
m_storage = std::move(temp);
181+
// m_size remains the same
182+
}
183+
184+
constexpr operator std::span<T const>() const {
185+
return std::span<T const>(data(), static_cast<std::size_t>(size()));
186+
}
187+
constexpr operator std::span<T>() {
188+
return std::span<T>(data(), static_cast<std::size_t>(size()));
189+
}
190+
191+
private:
192+
static constexpr auto reservation_size(auto const source_size) {
193+
return bounded::assume_in_range<capacity_type>(bounded::max(source_size, bounded::constant<min_capacity>));
194+
}
195+
constexpr explicit bounded_vector(auto && source, auto const source_size):
196+
m_storage(reservation_size(source_size)),
197+
m_size(bounded::assume_in_range<size_type>(source_size))
198+
{
199+
::containers::uninitialized_copy_no_overlap(OPERATORS_FORWARD(source), data());
200+
}
201+
202+
using capacity_type = bounded::integer<
203+
bounded::normalize<min_capacity>,
204+
bounded::normalize<max_capacity>
205+
>;
206+
using storage_type = uninitialized_dynamic_array<T, capacity_type>;
207+
[[no_unique_address]] storage_type m_storage;
208+
[[no_unique_address]] size_type m_size = 0_bi;
209+
};
210+
211+
} // namespace containers

source/containers/containers.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export import containers.assign_to_empty_into_capacity;
5353
export import containers.at;
5454
export import containers.begin_end;
5555
export import containers.bidirectional_range;
56+
export import containers.bounded_vector;
5657
export import containers.c_array;
5758
export import containers.can_set_size;
5859
export import containers.clear;

0 commit comments

Comments
 (0)