blob: 457095db762c6787ab8c9940ae5330b14ce0955f [file] [log] [blame]
Dominik Laskowski6fdf1142020-10-07 12:09:09 -07001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
Dominik Laskowski0bacf272020-10-22 14:08:27 -070019#include <ftl/ArrayTraits.h>
20
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070021#include <algorithm>
22#include <cassert>
23#include <iterator>
24#include <memory>
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070025#include <type_traits>
26#include <utility>
27
28namespace android::ftl {
29
Dominik Laskowski03572372020-10-27 22:36:00 -070030constexpr struct IteratorRangeTag {} IteratorRange;
31
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070032// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
33// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
34// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
35// default constructor, since elements are constructed in-place as the vector grows. Operations that
Dominik Laskowski03572372020-10-27 22:36:00 -070036// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
37// adheres to standard containers, except the unstable_erase operation that does not preserve order,
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070038// and the replace operation that destructively emplaces.
39//
40// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
41//
42// Example usage:
43//
44// ftl::StaticVector<char, 3> vector;
45// assert(vector.empty());
46//
47// vector = {'a', 'b'};
48// assert(vector.size() == 2u);
49//
50// vector.push_back('c');
51// assert(vector.full());
52//
53// assert(!vector.push_back('d'));
54// assert(vector.size() == 3u);
55//
56// vector.unstable_erase(vector.begin());
57// assert(vector == (ftl::StaticVector{'c', 'b'}));
58//
59// vector.pop_back();
60// assert(vector.back() == 'c');
61//
62// const char array[] = "hi";
63// vector = ftl::StaticVector(array);
64// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
65//
66template <typename T, size_t N>
Dominik Laskowski0bacf272020-10-22 14:08:27 -070067class StaticVector final : ArrayTraits<T>,
68 ArrayIterators<StaticVector<T, N>, T>,
69 ArrayComparators<StaticVector> {
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070070 static_assert(N > 0);
71
Dominik Laskowski0bacf272020-10-22 14:08:27 -070072 using ArrayTraits<T>::construct_at;
73
74 using Iter = ArrayIterators<StaticVector, T>;
75 friend Iter;
76
Dominik Laskowski03572372020-10-27 22:36:00 -070077 // There is ambiguity when constructing from two iterator-like elements like pointers:
78 // they could be an iterator range, or arguments for in-place construction. Assume the
79 // latter unless they are input iterators and cannot be used to construct elements. If
80 // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
81 template <typename I, typename Traits = std::iterator_traits<I>>
82 using IsInputIterator = std::conjunction<
83 std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
84 std::negation<std::is_constructible<T, I>>>;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070085
86public:
Dominik Laskowski0bacf272020-10-22 14:08:27 -070087 FTL_ARRAY_TRAIT(T, value_type);
88 FTL_ARRAY_TRAIT(T, size_type);
89 FTL_ARRAY_TRAIT(T, difference_type);
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070090
Dominik Laskowski0bacf272020-10-22 14:08:27 -070091 FTL_ARRAY_TRAIT(T, pointer);
92 FTL_ARRAY_TRAIT(T, reference);
93 FTL_ARRAY_TRAIT(T, iterator);
94 FTL_ARRAY_TRAIT(T, reverse_iterator);
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070095
Dominik Laskowski0bacf272020-10-22 14:08:27 -070096 FTL_ARRAY_TRAIT(T, const_pointer);
97 FTL_ARRAY_TRAIT(T, const_reference);
98 FTL_ARRAY_TRAIT(T, const_iterator);
99 FTL_ARRAY_TRAIT(T, const_reverse_iterator);
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700100
101 // Creates an empty vector.
102 StaticVector() = default;
103
104 // Copies and moves a vector, respectively.
Dominik Laskowski03572372020-10-27 22:36:00 -0700105 StaticVector(const StaticVector& other)
106 : StaticVector(IteratorRange, other.begin(), other.end()) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700107 StaticVector(StaticVector&& other) { swap<Empty>(other); }
108
109 // Copies at most N elements from a smaller convertible vector.
110 template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
Dominik Laskowski03572372020-10-27 22:36:00 -0700111 StaticVector(const StaticVector<U, M>& other)
112 : StaticVector(IteratorRange, other.begin(), other.end()) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700113
114 // Copies at most N elements from an array.
115 template <typename U, size_t M>
Dominik Laskowski03572372020-10-27 22:36:00 -0700116 explicit StaticVector(U (&array)[M])
117 : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700118
119 // Copies at most N elements from the range [first, last).
Dominik Laskowski03572372020-10-27 22:36:00 -0700120 //
121 // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
122 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700123 template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
Dominik Laskowski03572372020-10-27 22:36:00 -0700124 StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
125 using V = typename std::iterator_traits<Iterator>::value_type;
126 static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
127 }
128
129 template <typename Iterator>
130 StaticVector(IteratorRangeTag, Iterator first, Iterator last)
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700131 : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
132 std::uninitialized_copy(first, first + mSize, begin());
133 }
134
135 // Constructs at most N elements. The template arguments T and N are inferred using the
136 // deduction guide defined below. Note that T is determined from the first element, and
137 // subsequent elements must have convertible types:
138 //
139 // ftl::StaticVector vector = {1, 2, 3};
140 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
141 //
142 // const auto copy = "quince"s;
143 // auto move = "tart"s;
144 // ftl::StaticVector vector = {copy, std::move(move)};
145 //
146 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
147 //
148 template <typename E, typename... Es,
149 typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
150 StaticVector(E&& element, Es&&... elements)
151 : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
152 std::forward<Es>(elements)...) {
153 static_assert(sizeof...(elements) < N, "Too many elements");
154 }
155
156 // Constructs at most N elements. The template arguments T and N are inferred using the
157 // deduction guide defined below. Element types must be convertible to the specified T:
158 //
159 // ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
160 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
161 //
162 template <typename... Es>
163 explicit StaticVector(std::in_place_type_t<T>, Es... elements)
164 : StaticVector(std::forward<Es>(elements)...) {}
165
166 ~StaticVector() { std::destroy(begin(), end()); }
167
168 StaticVector& operator=(const StaticVector& other) {
169 StaticVector copy(other);
170 swap(copy);
171 return *this;
172 }
173
174 StaticVector& operator=(StaticVector&& other) {
175 std::destroy(begin(), end());
176 mSize = 0;
177 swap<Empty>(other);
178 return *this;
179 }
180
181 template <typename = void>
182 void swap(StaticVector&);
183
Dominik Laskowski03572372020-10-27 22:36:00 -0700184 static constexpr size_type max_size() { return N; }
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700185 size_type size() const { return mSize; }
186
187 bool empty() const { return size() == 0; }
188 bool full() const { return size() == max_size(); }
189
190 iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700191 iterator end() { return begin() + size(); }
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700192
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700193 using Iter::begin;
194 using Iter::end;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700195
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700196 using Iter::cbegin;
197 using Iter::cend;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700198
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700199 using Iter::rbegin;
200 using Iter::rend;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700201
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700202 using Iter::crbegin;
203 using Iter::crend;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700204
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700205 using Iter::last;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700206
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700207 using Iter::back;
208 using Iter::front;
209
210 using Iter::operator[];
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700211
Dominik Laskowski03572372020-10-27 22:36:00 -0700212 // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
213 // replacing at end() is erroneous.
214 //
215 // The element is emplaced via move constructor, so type T does not need to define copy/move
216 // assignment, e.g. its data members may be const.
217 //
218 // The arguments may directly or indirectly refer to the element being replaced.
219 //
220 // Iterators to the replaced element point to its replacement, and others remain valid.
221 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700222 template <typename... Args>
Dominik Laskowski03572372020-10-27 22:36:00 -0700223 reference replace(const_iterator it, Args&&... args) {
224 value_type element{std::forward<Args>(args)...};
225 std::destroy_at(it);
226 // This is only safe because exceptions are disabled.
227 return *construct_at(it, std::move(element));
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700228 }
229
230 // Appends an element, and returns an iterator to it. If the vector is full, the element is not
Dominik Laskowski03572372020-10-27 22:36:00 -0700231 // inserted, and the end() iterator is returned.
232 //
233 // On success, the end() iterator is invalidated.
234 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700235 template <typename... Args>
236 iterator emplace_back(Args&&... args) {
237 if (full()) return end();
238 const iterator it = construct_at(end(), std::forward<Args>(args)...);
239 ++mSize;
240 return it;
241 }
242
Dominik Laskowski03572372020-10-27 22:36:00 -0700243 // Appends an element unless the vector is full, and returns whether the element was inserted.
244 //
245 // On success, the end() iterator is invalidated.
246 //
Dominik Laskowski0bacf272020-10-22 14:08:27 -0700247 bool push_back(const value_type& v) {
248 // Two statements for sequence point.
249 const iterator it = emplace_back(v);
250 return it != end();
251 }
252
253 bool push_back(value_type&& v) {
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700254 // Two statements for sequence point.
255 const iterator it = emplace_back(std::move(v));
256 return it != end();
257 }
258
Dominik Laskowski03572372020-10-27 22:36:00 -0700259 // Removes the last element. The vector must not be empty, or the call is erroneous.
260 //
261 // The last() and end() iterators are invalidated.
262 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700263 void pop_back() { unstable_erase(last()); }
264
Dominik Laskowski03572372020-10-27 22:36:00 -0700265 // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
266 // this moves the last element to the slot of the erased element.
267 //
268 // The last() and end() iterators, as well as those to the erased element, are invalidated.
269 //
270 void unstable_erase(const_iterator it) {
271 std::destroy_at(it);
272 if (it != last()) {
273 // Move last element and destroy its source for destructor side effects. This is only
274 // safe because exceptions are disabled.
275 construct_at(it, std::move(back()));
276 std::destroy_at(last());
277 }
278 --mSize;
279 }
280
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700281private:
282 struct Empty {};
283
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700284 // Recursion for variadic constructor.
285 template <size_t I, typename E, typename... Es>
286 StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
287 : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
288 construct_at(begin() + I, std::forward<E>(element));
289 }
290
291 // Base case for variadic constructor.
292 template <size_t I>
293 explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
294
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700295 size_type mSize = 0;
296 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
297};
298
299// Deduction guide for array constructor.
300template <typename T, size_t N>
301StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
302
303// Deduction guide for variadic constructor.
304template <typename T, typename... Us, typename V = std::decay_t<T>,
305 typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
306StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
307
308// Deduction guide for in-place constructor.
309template <typename T, typename... Us>
310StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
311
312template <typename T, size_t N>
313template <typename E>
314void StaticVector<T, N>::swap(StaticVector& other) {
315 auto [to, from] = std::make_pair(this, &other);
316 if (from == this) return;
317
318 // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
319 auto [min, max] = std::make_pair(size(), other.size());
320
321 // No elements to swap if moving into an empty vector.
322 if constexpr (std::is_same_v<E, Empty>) {
323 assert(min == 0);
324 } else {
325 if (min > max) {
326 std::swap(from, to);
327 std::swap(min, max);
328 }
329
330 // Swap elements [0, min).
331 std::swap_ranges(begin(), begin() + min, other.begin());
332
333 // No elements to move if sizes are equal.
334 if (min == max) return;
335 }
336
337 // Move elements [min, max) and destroy their source for destructor side effects.
338 const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
339 std::uninitialized_move(first, last, to->begin() + min);
340 std::destroy(first, last);
341
342 std::swap(mSize, other.mSize);
343}
344
345template <typename T, size_t N>
346inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
347 lhs.swap(rhs);
348}
349
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700350} // namespace android::ftl