blob: c0cdd1180334ee374a39f4a4b82171693568f5ec [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
19#include <algorithm>
20#include <cassert>
21#include <iterator>
22#include <memory>
23#include <new>
24#include <type_traits>
25#include <utility>
26
27namespace android::ftl {
28
Dominik Laskowski03572372020-10-27 22:36:00 -070029constexpr struct IteratorRangeTag {} IteratorRange;
30
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070031// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
32// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
33// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
34// default constructor, since elements are constructed in-place as the vector grows. Operations that
Dominik Laskowski03572372020-10-27 22:36:00 -070035// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
36// adheres to standard containers, except the unstable_erase operation that does not preserve order,
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070037// and the replace operation that destructively emplaces.
38//
39// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
40//
41// Example usage:
42//
43// ftl::StaticVector<char, 3> vector;
44// assert(vector.empty());
45//
46// vector = {'a', 'b'};
47// assert(vector.size() == 2u);
48//
49// vector.push_back('c');
50// assert(vector.full());
51//
52// assert(!vector.push_back('d'));
53// assert(vector.size() == 3u);
54//
55// vector.unstable_erase(vector.begin());
56// assert(vector == (ftl::StaticVector{'c', 'b'}));
57//
58// vector.pop_back();
59// assert(vector.back() == 'c');
60//
61// const char array[] = "hi";
62// vector = ftl::StaticVector(array);
63// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
64//
65template <typename T, size_t N>
Dominik Laskowski03572372020-10-27 22:36:00 -070066class StaticVector final {
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070067 static_assert(N > 0);
68
Dominik Laskowski03572372020-10-27 22:36:00 -070069 // There is ambiguity when constructing from two iterator-like elements like pointers:
70 // they could be an iterator range, or arguments for in-place construction. Assume the
71 // latter unless they are input iterators and cannot be used to construct elements. If
72 // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
73 template <typename I, typename Traits = std::iterator_traits<I>>
74 using IsInputIterator = std::conjunction<
75 std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
76 std::negation<std::is_constructible<T, I>>>;
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070077
78public:
79 using value_type = T;
80 using size_type = size_t;
81 using difference_type = ptrdiff_t;
82
83 using pointer = value_type*;
84 using reference = value_type&;
85 using iterator = pointer;
86 using reverse_iterator = std::reverse_iterator<iterator>;
87
88 using const_pointer = const value_type*;
89 using const_reference = const value_type&;
90 using const_iterator = const_pointer;
91 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
92
93 // Creates an empty vector.
94 StaticVector() = default;
95
96 // Copies and moves a vector, respectively.
Dominik Laskowski03572372020-10-27 22:36:00 -070097 StaticVector(const StaticVector& other)
98 : StaticVector(IteratorRange, other.begin(), other.end()) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -070099 StaticVector(StaticVector&& other) { swap<Empty>(other); }
100
101 // Copies at most N elements from a smaller convertible vector.
102 template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
Dominik Laskowski03572372020-10-27 22:36:00 -0700103 StaticVector(const StaticVector<U, M>& other)
104 : StaticVector(IteratorRange, other.begin(), other.end()) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700105
106 // Copies at most N elements from an array.
107 template <typename U, size_t M>
Dominik Laskowski03572372020-10-27 22:36:00 -0700108 explicit StaticVector(U (&array)[M])
109 : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700110
111 // Copies at most N elements from the range [first, last).
Dominik Laskowski03572372020-10-27 22:36:00 -0700112 //
113 // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
114 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700115 template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
Dominik Laskowski03572372020-10-27 22:36:00 -0700116 StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
117 using V = typename std::iterator_traits<Iterator>::value_type;
118 static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
119 }
120
121 template <typename Iterator>
122 StaticVector(IteratorRangeTag, Iterator first, Iterator last)
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700123 : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
124 std::uninitialized_copy(first, first + mSize, begin());
125 }
126
127 // Constructs at most N elements. The template arguments T and N are inferred using the
128 // deduction guide defined below. Note that T is determined from the first element, and
129 // subsequent elements must have convertible types:
130 //
131 // ftl::StaticVector vector = {1, 2, 3};
132 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
133 //
134 // const auto copy = "quince"s;
135 // auto move = "tart"s;
136 // ftl::StaticVector vector = {copy, std::move(move)};
137 //
138 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
139 //
140 template <typename E, typename... Es,
141 typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
142 StaticVector(E&& element, Es&&... elements)
143 : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
144 std::forward<Es>(elements)...) {
145 static_assert(sizeof...(elements) < N, "Too many elements");
146 }
147
148 // Constructs at most N elements. The template arguments T and N are inferred using the
149 // deduction guide defined below. Element types must be convertible to the specified T:
150 //
151 // ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
152 // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
153 //
154 template <typename... Es>
155 explicit StaticVector(std::in_place_type_t<T>, Es... elements)
156 : StaticVector(std::forward<Es>(elements)...) {}
157
158 ~StaticVector() { std::destroy(begin(), end()); }
159
160 StaticVector& operator=(const StaticVector& other) {
161 StaticVector copy(other);
162 swap(copy);
163 return *this;
164 }
165
166 StaticVector& operator=(StaticVector&& other) {
167 std::destroy(begin(), end());
168 mSize = 0;
169 swap<Empty>(other);
170 return *this;
171 }
172
173 template <typename = void>
174 void swap(StaticVector&);
175
Dominik Laskowski03572372020-10-27 22:36:00 -0700176 static constexpr size_type max_size() { return N; }
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700177 size_type size() const { return mSize; }
178
179 bool empty() const { return size() == 0; }
180 bool full() const { return size() == max_size(); }
181
182 iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
183 const_iterator begin() const { return cbegin(); }
184 const_iterator cbegin() const { return mut().begin(); }
185
186 iterator end() { return begin() + size(); }
187 const_iterator end() const { return cend(); }
188 const_iterator cend() const { return mut().end(); }
189
190 reverse_iterator rbegin() { return std::make_reverse_iterator(end()); }
191 const_reverse_iterator rbegin() const { return crbegin(); }
192 const_reverse_iterator crbegin() const { return mut().rbegin(); }
193
194 reverse_iterator rend() { return std::make_reverse_iterator(begin()); }
195 const_reverse_iterator rend() const { return crend(); }
196 const_reverse_iterator crend() const { return mut().rend(); }
197
198 iterator last() { return end() - 1; }
199 const_iterator last() const { return mut().last(); }
200
201 reference front() { return *begin(); }
202 const_reference front() const { return mut().front(); }
203
204 reference back() { return *last(); }
205 const_reference back() const { return mut().back(); }
206
207 reference operator[](size_type i) { return *(begin() + i); }
208 const_reference operator[](size_type i) const { return mut()[i]; }
209
Dominik Laskowski03572372020-10-27 22:36:00 -0700210 // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
211 // replacing at end() is erroneous.
212 //
213 // The element is emplaced via move constructor, so type T does not need to define copy/move
214 // assignment, e.g. its data members may be const.
215 //
216 // The arguments may directly or indirectly refer to the element being replaced.
217 //
218 // Iterators to the replaced element point to its replacement, and others remain valid.
219 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700220 template <typename... Args>
Dominik Laskowski03572372020-10-27 22:36:00 -0700221 reference replace(const_iterator it, Args&&... args) {
222 value_type element{std::forward<Args>(args)...};
223 std::destroy_at(it);
224 // This is only safe because exceptions are disabled.
225 return *construct_at(it, std::move(element));
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700226 }
227
228 // 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 -0700229 // inserted, and the end() iterator is returned.
230 //
231 // On success, the end() iterator is invalidated.
232 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700233 template <typename... Args>
234 iterator emplace_back(Args&&... args) {
235 if (full()) return end();
236 const iterator it = construct_at(end(), std::forward<Args>(args)...);
237 ++mSize;
238 return it;
239 }
240
Dominik Laskowski03572372020-10-27 22:36:00 -0700241 // Appends an element unless the vector is full, and returns whether the element was inserted.
242 //
243 // On success, the end() iterator is invalidated.
244 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700245 bool push_back(value_type v) {
246 // Two statements for sequence point.
247 const iterator it = emplace_back(std::move(v));
248 return it != end();
249 }
250
Dominik Laskowski03572372020-10-27 22:36:00 -0700251 // Removes the last element. The vector must not be empty, or the call is erroneous.
252 //
253 // The last() and end() iterators are invalidated.
254 //
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700255 void pop_back() { unstable_erase(last()); }
256
Dominik Laskowski03572372020-10-27 22:36:00 -0700257 // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
258 // this moves the last element to the slot of the erased element.
259 //
260 // The last() and end() iterators, as well as those to the erased element, are invalidated.
261 //
262 void unstable_erase(const_iterator it) {
263 std::destroy_at(it);
264 if (it != last()) {
265 // Move last element and destroy its source for destructor side effects. This is only
266 // safe because exceptions are disabled.
267 construct_at(it, std::move(back()));
268 std::destroy_at(last());
269 }
270 --mSize;
271 }
272
Dominik Laskowski6fdf1142020-10-07 12:09:09 -0700273private:
274 struct Empty {};
275
276 StaticVector& mut() const { return *const_cast<StaticVector*>(this); }
277
278 // Recursion for variadic constructor.
279 template <size_t I, typename E, typename... Es>
280 StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
281 : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
282 construct_at(begin() + I, std::forward<E>(element));
283 }
284
285 // Base case for variadic constructor.
286 template <size_t I>
287 explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
288
289 // TODO: Replace with std::construct_at in C++20.
290 template <typename... Args>
291 static pointer construct_at(const_iterator it, Args&&... args) {
292 void* const ptr = const_cast<void*>(static_cast<const void*>(it));
293 return new (ptr) value_type{std::forward<Args>(args)...};
294 }
295
296 size_type mSize = 0;
297 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
298};
299
300// Deduction guide for array constructor.
301template <typename T, size_t N>
302StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
303
304// Deduction guide for variadic constructor.
305template <typename T, typename... Us, typename V = std::decay_t<T>,
306 typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
307StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
308
309// Deduction guide for in-place constructor.
310template <typename T, typename... Us>
311StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
312
313template <typename T, size_t N>
314template <typename E>
315void StaticVector<T, N>::swap(StaticVector& other) {
316 auto [to, from] = std::make_pair(this, &other);
317 if (from == this) return;
318
319 // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
320 auto [min, max] = std::make_pair(size(), other.size());
321
322 // No elements to swap if moving into an empty vector.
323 if constexpr (std::is_same_v<E, Empty>) {
324 assert(min == 0);
325 } else {
326 if (min > max) {
327 std::swap(from, to);
328 std::swap(min, max);
329 }
330
331 // Swap elements [0, min).
332 std::swap_ranges(begin(), begin() + min, other.begin());
333
334 // No elements to move if sizes are equal.
335 if (min == max) return;
336 }
337
338 // Move elements [min, max) and destroy their source for destructor side effects.
339 const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
340 std::uninitialized_move(first, last, to->begin() + min);
341 std::destroy(first, last);
342
343 std::swap(mSize, other.mSize);
344}
345
346template <typename T, size_t N>
347inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
348 lhs.swap(rhs);
349}
350
351// TODO: Replace with operator<=> in C++20.
352template <typename T, size_t N, size_t M>
353inline bool operator==(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
354 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
355}
356
357template <typename T, size_t N, size_t M>
358inline bool operator<(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
359 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
360}
361
362template <typename T, size_t N, size_t M>
363inline bool operator>(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
364 return rhs < lhs;
365}
366
367template <typename T, size_t N, size_t M>
368inline bool operator!=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
369 return !(lhs == rhs);
370}
371
372template <typename T, size_t N, size_t M>
373inline bool operator>=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
374 return !(lhs < rhs);
375}
376
377template <typename T, size_t N, size_t M>
378inline bool operator<=(const StaticVector<T, N>& lhs, const StaticVector<T, M>& rhs) {
379 return !(rhs < lhs);
380}
381
382} // namespace android::ftl