1 /* 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 <ftl/details/array_traits.h> 20 #include <ftl/static_vector.h> 21 22 #include <algorithm> 23 #include <iterator> 24 #include <utility> 25 #include <variant> 26 #include <vector> 27 28 #include <ftl/details/type_traits.h> 29 30 namespace android::ftl { 31 32 template <typename> 33 struct is_small_vector; 34 35 // ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement 36 // for std::vector with statically allocated storage for N elements, whose goal is to improve run 37 // time by avoiding heap allocation and increasing probability of cache hits. The standard API is 38 // augmented by an unstable_erase operation that does not preserve order, and a replace operation 39 // that destructively emplaces. 40 // 41 // Unlike std::vector, T does not require copy/move assignment, so may be an object with const data 42 // members, or be const itself. 43 // 44 // SmallVector<T, 0> is a specialization that thinly wraps std::vector. 45 // 46 // Example usage: 47 // 48 // ftl::SmallVector<char, 3> vector; 49 // assert(vector.empty()); 50 // assert(!vector.dynamic()); 51 // 52 // vector = {'a', 'b', 'c'}; 53 // assert(vector.size() == 3u); 54 // assert(!vector.dynamic()); 55 // 56 // vector.push_back('d'); 57 // assert(vector.dynamic()); 58 // 59 // vector.unstable_erase(vector.begin()); 60 // assert(vector == (ftl::SmallVector{'d', 'b', 'c'})); 61 // 62 // vector.pop_back(); 63 // assert(vector.back() == 'b'); 64 // assert(vector.dynamic()); 65 // 66 // const char array[] = "hi"; 67 // vector = ftl::SmallVector(array); 68 // assert(vector == (ftl::SmallVector{'h', 'i', '\0'})); 69 // assert(!vector.dynamic()); 70 // 71 // ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?'); 72 // assert(strings.size() == 3u); 73 // assert(!strings.dynamic()); 74 // 75 // assert(strings[0] == "abc"); 76 // assert(strings[1] == "123"); 77 // assert(strings[2] == "???"); 78 // 79 template <typename T, std::size_t N> 80 class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<SmallVector> { 81 using Static = StaticVector<T, N>; 82 using Dynamic = SmallVector<T, 0>; 83 84 public: 85 FTL_ARRAY_TRAIT(T, value_type); 86 FTL_ARRAY_TRAIT(T, size_type); 87 FTL_ARRAY_TRAIT(T, difference_type); 88 89 FTL_ARRAY_TRAIT(T, pointer); 90 FTL_ARRAY_TRAIT(T, reference); 91 FTL_ARRAY_TRAIT(T, iterator); 92 FTL_ARRAY_TRAIT(T, reverse_iterator); 93 94 FTL_ARRAY_TRAIT(T, const_pointer); 95 FTL_ARRAY_TRAIT(T, const_reference); 96 FTL_ARRAY_TRAIT(T, const_iterator); 97 FTL_ARRAY_TRAIT(T, const_reverse_iterator); 98 99 // Creates an empty vector. 100 SmallVector() = default; 101 102 // Constructs at most N elements. See StaticVector for underlying constructors. 103 template <typename Arg, typename... Args, 104 typename = std::enable_if_t<!is_small_vector<details::remove_cvref_t<Arg>>{}>> SmallVector(Arg && arg,Args &&...args)105 SmallVector(Arg&& arg, Args&&... args) 106 : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {} 107 108 // Copies or moves elements from a smaller convertible vector. 109 template <typename U, std::size_t M, typename = std::enable_if_t<(M > 0)>> SmallVector(SmallVector<U,M> other)110 SmallVector(SmallVector<U, M> other) : vector_(convert(std::move(other))) {} 111 swap(SmallVector & other)112 void swap(SmallVector& other) { vector_.swap(other.vector_); } 113 114 // Returns whether the vector is backed by static or dynamic storage. dynamic()115 bool dynamic() const { return std::holds_alternative<Dynamic>(vector_); } 116 117 // Avoid std::visit as it generates a dispatch table. 118 #define DISPATCH(T, F, ...) \ 119 T F() __VA_ARGS__ { \ 120 return dynamic() ? std::get<Dynamic>(vector_).F() : std::get<Static>(vector_).F(); \ 121 } 122 DISPATCH(size_type,max_size,const)123 DISPATCH(size_type, max_size, const) 124 DISPATCH(size_type, size, const) 125 DISPATCH(bool, empty, const) 126 127 // noexcept to suppress warning about zero variadic macro arguments. 128 DISPATCH(iterator, begin, noexcept) 129 DISPATCH(const_iterator, begin, const) 130 DISPATCH(const_iterator, cbegin, const) 131 132 DISPATCH(iterator, end, noexcept) 133 DISPATCH(const_iterator, end, const) 134 DISPATCH(const_iterator, cend, const) 135 136 DISPATCH(reverse_iterator, rbegin, noexcept) 137 DISPATCH(const_reverse_iterator, rbegin, const) 138 DISPATCH(const_reverse_iterator, crbegin, const) 139 140 DISPATCH(reverse_iterator, rend, noexcept) 141 DISPATCH(const_reverse_iterator, rend, const) 142 DISPATCH(const_reverse_iterator, crend, const) 143 144 DISPATCH(iterator, last, noexcept) 145 DISPATCH(const_iterator, last, const) 146 147 DISPATCH(reference, front, noexcept) 148 DISPATCH(const_reference, front, const) 149 150 DISPATCH(reference, back, noexcept) 151 DISPATCH(const_reference, back, const) 152 153 reference operator[](size_type i) { 154 return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i]; 155 } 156 157 const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; } 158 159 // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so 160 // replacing at end() is erroneous. 161 // 162 // The element is emplaced via move constructor, so type T does not need to define copy/move 163 // assignment, e.g. its data members may be const. 164 // 165 // The arguments may directly or indirectly refer to the element being replaced. 166 // 167 // Iterators to the replaced element point to its replacement, and others remain valid. 168 // 169 template <typename... Args> replace(const_iterator it,Args &&...args)170 reference replace(const_iterator it, Args&&... args) { 171 if (dynamic()) { 172 return std::get<Dynamic>(vector_).replace(it, std::forward<Args>(args)...); 173 } else { 174 return std::get<Static>(vector_).replace(it, std::forward<Args>(args)...); 175 } 176 } 177 178 // Appends an element, and returns a reference to it. 179 // 180 // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. 181 // Otherwise, only the end() iterator is invalidated. 182 // 183 template <typename... Args> emplace_back(Args &&...args)184 reference emplace_back(Args&&... args) { 185 constexpr auto kInsertStatic = &Static::template emplace_back<Args...>; 186 constexpr auto kInsertDynamic = &Dynamic::template emplace_back<Args...>; 187 return *insert<kInsertStatic, kInsertDynamic>(std::forward<Args>(args)...); 188 } 189 190 // Appends an element. 191 // 192 // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. 193 // Otherwise, only the end() iterator is invalidated. 194 // push_back(const value_type & v)195 void push_back(const value_type& v) { 196 constexpr auto kInsertStatic = 197 static_cast<bool (Static::*)(const value_type&)>(&Static::push_back); 198 constexpr auto kInsertDynamic = 199 static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back); 200 insert<kInsertStatic, kInsertDynamic>(v); 201 } 202 push_back(value_type && v)203 void push_back(value_type&& v) { 204 constexpr auto kInsertStatic = static_cast<bool (Static::*)(value_type &&)>(&Static::push_back); 205 constexpr auto kInsertDynamic = 206 static_cast<bool (Dynamic::*)(value_type &&)>(&Dynamic::push_back); 207 insert<kInsertStatic, kInsertDynamic>(std::move(v)); 208 } 209 210 // Removes the last element. The vector must not be empty, or the call is erroneous. 211 // 212 // The last() and end() iterators are invalidated. 213 // DISPATCH(void,pop_back,noexcept)214 DISPATCH(void, pop_back, noexcept) 215 216 // Removes all elements. 217 // 218 // All iterators are invalidated. 219 // 220 DISPATCH(void, clear, noexcept) 221 222 #undef DISPATCH 223 224 // Erases an element, but does not preserve order. Rather than shifting subsequent elements, 225 // this moves the last element to the slot of the erased element. 226 // 227 // The last() and end() iterators, as well as those to the erased element, are invalidated. 228 // 229 void unstable_erase(iterator it) { 230 if (dynamic()) { 231 std::get<Dynamic>(vector_).unstable_erase(it); 232 } else { 233 std::get<Static>(vector_).unstable_erase(it); 234 } 235 } 236 237 // Extracts the elements as std::vector. promote()238 std::vector<T> promote() && { 239 if (dynamic()) { 240 return std::get<Dynamic>(std::move(vector_)).promote(); 241 } else { 242 return {std::make_move_iterator(begin()), std::make_move_iterator(end())}; 243 } 244 } 245 246 private: 247 template <typename, std::size_t> 248 friend class SmallVector; 249 250 template <typename U, std::size_t M> convert(SmallVector<U,M> && other)251 static std::variant<Static, Dynamic> convert(SmallVector<U, M>&& other) { 252 using Other = SmallVector<U, M>; 253 254 if (other.dynamic()) { 255 return std::get<typename Other::Dynamic>(std::move(other.vector_)); 256 } else { 257 return std::get<typename Other::Static>(std::move(other.vector_)); 258 } 259 } 260 261 template <auto InsertStatic, auto InsertDynamic, typename... Args> insert(Args &&...args)262 auto insert(Args&&... args) { 263 if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) { 264 return (vector->*InsertDynamic)(std::forward<Args>(args)...); 265 } 266 267 auto& vector = std::get<Static>(vector_); 268 if (vector.full()) { 269 return (promote(vector).*InsertDynamic)(std::forward<Args>(args)...); 270 } else { 271 return (vector.*InsertStatic)(std::forward<Args>(args)...); 272 } 273 } 274 promote(Static & static_vector)275 Dynamic& promote(Static& static_vector) { 276 assert(static_vector.full()); 277 278 // Allocate double capacity to reduce probability of reallocation. 279 Dynamic vector; 280 vector.reserve(Static::max_size() * 2); 281 std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector)); 282 283 return vector_.template emplace<Dynamic>(std::move(vector)); 284 } 285 286 std::variant<Static, Dynamic> vector_; 287 }; 288 289 // Partial specialization without static storage. 290 template <typename T> 291 class SmallVector<T, 0> final : details::ArrayTraits<T>, 292 details::ArrayComparators<SmallVector>, 293 details::ArrayIterators<SmallVector<T, 0>, T>, 294 std::vector<T> { 295 using details::ArrayTraits<T>::replace_at; 296 297 using Iter = details::ArrayIterators<SmallVector, T>; 298 using Impl = std::vector<T>; 299 300 friend Iter; 301 302 public: 303 FTL_ARRAY_TRAIT(T, value_type); 304 FTL_ARRAY_TRAIT(T, size_type); 305 FTL_ARRAY_TRAIT(T, difference_type); 306 307 FTL_ARRAY_TRAIT(T, pointer); 308 FTL_ARRAY_TRAIT(T, reference); 309 FTL_ARRAY_TRAIT(T, iterator); 310 FTL_ARRAY_TRAIT(T, reverse_iterator); 311 312 FTL_ARRAY_TRAIT(T, const_pointer); 313 FTL_ARRAY_TRAIT(T, const_reference); 314 FTL_ARRAY_TRAIT(T, const_iterator); 315 FTL_ARRAY_TRAIT(T, const_reverse_iterator); 316 317 // See std::vector for underlying constructors. 318 using Impl::Impl; 319 320 // Copies and moves a vector, respectively. 321 SmallVector(const SmallVector&) = default; 322 SmallVector(SmallVector&&) = default; 323 324 // Constructs elements in place. See StaticVector for underlying constructor. 325 template <typename U, std::size_t... Sizes, typename... Types> SmallVector(InitializerList<U,std::index_sequence<Sizes...>,Types...> && list)326 SmallVector(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list) 327 : SmallVector(SmallVector<T, sizeof...(Sizes)>(std::move(list))) {} 328 329 // Copies or moves elements from a convertible vector. 330 template <typename U, std::size_t M> SmallVector(SmallVector<U,M> other)331 SmallVector(SmallVector<U, M> other) : Impl(convert(std::move(other))) {} 332 333 SmallVector& operator=(SmallVector other) { 334 // Define copy/move assignment in terms of copy/move construction. 335 swap(other); 336 return *this; 337 } 338 swap(SmallVector & other)339 void swap(SmallVector& other) { Impl::swap(other); } 340 341 using Impl::empty; 342 using Impl::max_size; 343 using Impl::size; 344 345 using Impl::reserve; 346 347 // std::vector iterators are not necessarily raw pointers. begin()348 iterator begin() { return Impl::data(); } end()349 iterator end() { return Impl::data() + size(); } 350 351 using Iter::begin; 352 using Iter::end; 353 354 using Iter::cbegin; 355 using Iter::cend; 356 357 using Iter::rbegin; 358 using Iter::rend; 359 360 using Iter::crbegin; 361 using Iter::crend; 362 363 using Iter::last; 364 365 using Iter::back; 366 using Iter::front; 367 368 using Iter::operator[]; 369 370 template <typename... Args> replace(const_iterator it,Args &&...args)371 reference replace(const_iterator it, Args&&... args) { 372 return replace_at(it, std::forward<Args>(args)...); 373 } 374 375 template <typename... Args> emplace_back(Args &&...args)376 iterator emplace_back(Args&&... args) { 377 return &Impl::emplace_back(std::forward<Args>(args)...); 378 } 379 push_back(const value_type & v)380 bool push_back(const value_type& v) { 381 Impl::push_back(v); 382 return true; 383 } 384 push_back(value_type && v)385 bool push_back(value_type&& v) { 386 Impl::push_back(std::move(v)); 387 return true; 388 } 389 390 using Impl::clear; 391 using Impl::pop_back; 392 unstable_erase(iterator it)393 void unstable_erase(iterator it) { 394 if (it != last()) replace(it, std::move(back())); 395 pop_back(); 396 } 397 promote()398 std::vector<T> promote() && { return std::move(*this); } 399 400 private: 401 template <typename U, std::size_t M> convert(SmallVector<U,M> && other)402 static Impl convert(SmallVector<U, M>&& other) { 403 if constexpr (std::is_constructible_v<Impl, std::vector<U>&&>) { 404 return std::move(other).promote(); 405 } else { 406 SmallVector vector(other.size()); 407 408 // Consistently with StaticVector, T only requires copy/move construction from U, rather than 409 // copy/move assignment. 410 auto it = vector.begin(); 411 for (auto& element : other) { 412 vector.replace(it++, std::move(element)); 413 } 414 415 return vector; 416 } 417 } 418 }; 419 420 template <typename> 421 struct is_small_vector : std::false_type {}; 422 423 template <typename T, std::size_t N> 424 struct is_small_vector<SmallVector<T, N>> : std::true_type {}; 425 426 // Deduction guide for array constructor. 427 template <typename T, std::size_t N> 428 SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>; 429 430 // Deduction guide for variadic constructor. 431 template <typename T, typename... Us, typename V = std::decay_t<T>, 432 typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>> 433 SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>; 434 435 // Deduction guide for in-place constructor. 436 template <typename T, std::size_t... Sizes, typename... Types> 437 SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&) 438 -> SmallVector<T, sizeof...(Sizes)>; 439 440 // Deduction guide for StaticVector conversion. 441 template <typename T, std::size_t N> 442 SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>; 443 444 template <typename T, std::size_t N> 445 inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) { 446 lhs.swap(rhs); 447 } 448 449 } // namespace android::ftl 450