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 DISPATCH(iterator, begin, ) 128 DISPATCH(const_iterator, begin, const) 129 DISPATCH(const_iterator, cbegin, const) 130 131 DISPATCH(iterator, end, ) 132 DISPATCH(const_iterator, end, const) 133 DISPATCH(const_iterator, cend, const) 134 135 DISPATCH(reverse_iterator, rbegin, ) 136 DISPATCH(const_reverse_iterator, rbegin, const) 137 DISPATCH(const_reverse_iterator, crbegin, const) 138 139 DISPATCH(reverse_iterator, rend, ) 140 DISPATCH(const_reverse_iterator, rend, const) 141 DISPATCH(const_reverse_iterator, crend, const) 142 143 DISPATCH(iterator, last, ) 144 DISPATCH(const_iterator, last, const) 145 146 DISPATCH(reference, front, ) 147 DISPATCH(const_reference, front, const) 148 149 DISPATCH(reference, back, ) 150 DISPATCH(const_reference, back, const) 151 152 reference operator[](size_type i) { 153 return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i]; 154 } 155 156 const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; } 157 158 // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so 159 // replacing at end() is erroneous. 160 // 161 // The element is emplaced via move constructor, so type T does not need to define copy/move 162 // assignment, e.g. its data members may be const. 163 // 164 // The arguments may directly or indirectly refer to the element being replaced. 165 // 166 // Iterators to the replaced element point to its replacement, and others remain valid. 167 // 168 template <typename... Args> replace(const_iterator it,Args &&...args)169 reference replace(const_iterator it, Args&&... args) { 170 if (dynamic()) { 171 return std::get<Dynamic>(vector_).replace(it, std::forward<Args>(args)...); 172 } else { 173 return std::get<Static>(vector_).replace(it, std::forward<Args>(args)...); 174 } 175 } 176 177 // Appends an element, and returns a reference to it. 178 // 179 // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. 180 // Otherwise, only the end() iterator is invalidated. 181 // 182 template <typename... Args> emplace_back(Args &&...args)183 reference emplace_back(Args&&... args) { 184 constexpr auto kInsertStatic = &Static::template emplace_back<Args...>; 185 constexpr auto kInsertDynamic = &Dynamic::template emplace_back<Args...>; 186 return *insert<kInsertStatic, kInsertDynamic>(std::forward<Args>(args)...); 187 } 188 189 // Appends an element. 190 // 191 // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. 192 // Otherwise, only the end() iterator is invalidated. 193 // push_back(const value_type & v)194 void push_back(const value_type& v) { 195 constexpr auto kInsertStatic = 196 static_cast<bool (Static::*)(const value_type&)>(&Static::push_back); 197 constexpr auto kInsertDynamic = 198 static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back); 199 insert<kInsertStatic, kInsertDynamic>(v); 200 } 201 push_back(value_type && v)202 void push_back(value_type&& v) { 203 constexpr auto kInsertStatic = static_cast<bool (Static::*)(value_type &&)>(&Static::push_back); 204 constexpr auto kInsertDynamic = 205 static_cast<bool (Dynamic::*)(value_type &&)>(&Dynamic::push_back); 206 insert<kInsertStatic, kInsertDynamic>(std::move(v)); 207 } 208 209 // Removes the last element. The vector must not be empty, or the call is erroneous. 210 // 211 // The last() and end() iterators are invalidated. 212 // 213 DISPATCH(void, pop_back, ) 214 215 // Removes all elements. 216 // 217 // All iterators are invalidated. 218 // 219 DISPATCH(void, clear, ) 220 221 #undef DISPATCH 222 223 // Erases an element, but does not preserve order. Rather than shifting subsequent elements, 224 // this moves the last element to the slot of the erased element. 225 // 226 // The last() and end() iterators, as well as those to the erased element, are invalidated. 227 // unstable_erase(iterator it)228 void unstable_erase(iterator it) { 229 if (dynamic()) { 230 std::get<Dynamic>(vector_).unstable_erase(it); 231 } else { 232 std::get<Static>(vector_).unstable_erase(it); 233 } 234 } 235 236 // Extracts the elements as std::vector. promote()237 std::vector<T> promote() && { 238 if (dynamic()) { 239 return std::get<Dynamic>(std::move(vector_)).promote(); 240 } else { 241 return {std::make_move_iterator(begin()), std::make_move_iterator(end())}; 242 } 243 } 244 245 private: 246 template <typename, std::size_t> 247 friend class SmallVector; 248 249 template <typename U, std::size_t M> convert(SmallVector<U,M> && other)250 static std::variant<Static, Dynamic> convert(SmallVector<U, M>&& other) { 251 using Other = SmallVector<U, M>; 252 253 if (other.dynamic()) { 254 return std::get<typename Other::Dynamic>(std::move(other.vector_)); 255 } else { 256 return std::get<typename Other::Static>(std::move(other.vector_)); 257 } 258 } 259 260 template <auto InsertStatic, auto InsertDynamic, typename... Args> insert(Args &&...args)261 auto insert(Args&&... args) { 262 if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) { 263 return (vector->*InsertDynamic)(std::forward<Args>(args)...); 264 } 265 266 auto& vector = std::get<Static>(vector_); 267 if (vector.full()) { 268 return (promote(vector).*InsertDynamic)(std::forward<Args>(args)...); 269 } else { 270 return (vector.*InsertStatic)(std::forward<Args>(args)...); 271 } 272 } 273 promote(Static & static_vector)274 Dynamic& promote(Static& static_vector) { 275 assert(static_vector.full()); 276 277 // Allocate double capacity to reduce probability of reallocation. 278 Dynamic vector; 279 vector.reserve(Static::max_size() * 2); 280 std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector)); 281 282 return vector_.template emplace<Dynamic>(std::move(vector)); 283 } 284 285 std::variant<Static, Dynamic> vector_; 286 }; 287 288 // Partial specialization without static storage. 289 template <typename T> 290 class SmallVector<T, 0> final : details::ArrayTraits<T>, 291 details::ArrayComparators<SmallVector>, 292 details::ArrayIterators<SmallVector<T, 0>, T>, 293 std::vector<T> { 294 using details::ArrayTraits<T>::replace_at; 295 296 using Iter = details::ArrayIterators<SmallVector, T>; 297 using Impl = std::vector<T>; 298 299 friend Iter; 300 301 public: 302 FTL_ARRAY_TRAIT(T, value_type); 303 FTL_ARRAY_TRAIT(T, size_type); 304 FTL_ARRAY_TRAIT(T, difference_type); 305 306 FTL_ARRAY_TRAIT(T, pointer); 307 FTL_ARRAY_TRAIT(T, reference); 308 FTL_ARRAY_TRAIT(T, iterator); 309 FTL_ARRAY_TRAIT(T, reverse_iterator); 310 311 FTL_ARRAY_TRAIT(T, const_pointer); 312 FTL_ARRAY_TRAIT(T, const_reference); 313 FTL_ARRAY_TRAIT(T, const_iterator); 314 FTL_ARRAY_TRAIT(T, const_reverse_iterator); 315 316 // See std::vector for underlying constructors. 317 using Impl::Impl; 318 319 // Copies and moves a vector, respectively. 320 SmallVector(const SmallVector&) = default; 321 SmallVector(SmallVector&&) = default; 322 323 // Constructs elements in place. See StaticVector for underlying constructor. 324 template <typename U, std::size_t... Sizes, typename... Types> SmallVector(InitializerList<U,std::index_sequence<Sizes...>,Types...> && list)325 SmallVector(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list) 326 : SmallVector(SmallVector<T, sizeof...(Sizes)>(std::move(list))) {} 327 328 // Copies or moves elements from a convertible vector. 329 template <typename U, std::size_t M> SmallVector(SmallVector<U,M> other)330 SmallVector(SmallVector<U, M> other) : Impl(convert(std::move(other))) {} 331 332 SmallVector& operator=(SmallVector other) { 333 // Define copy/move assignment in terms of copy/move construction. 334 swap(other); 335 return *this; 336 } 337 swap(SmallVector & other)338 void swap(SmallVector& other) { Impl::swap(other); } 339 340 using Impl::empty; 341 using Impl::max_size; 342 using Impl::size; 343 344 using Impl::reserve; 345 346 // std::vector iterators are not necessarily raw pointers. begin()347 iterator begin() { return Impl::data(); } end()348 iterator end() { return Impl::data() + size(); } 349 350 using Iter::begin; 351 using Iter::end; 352 353 using Iter::cbegin; 354 using Iter::cend; 355 356 using Iter::rbegin; 357 using Iter::rend; 358 359 using Iter::crbegin; 360 using Iter::crend; 361 362 using Iter::last; 363 364 using Iter::back; 365 using Iter::front; 366 367 using Iter::operator[]; 368 369 template <typename... Args> replace(const_iterator it,Args &&...args)370 reference replace(const_iterator it, Args&&... args) { 371 return replace_at(it, std::forward<Args>(args)...); 372 } 373 374 template <typename... Args> emplace_back(Args &&...args)375 iterator emplace_back(Args&&... args) { 376 return &Impl::emplace_back(std::forward<Args>(args)...); 377 } 378 push_back(const value_type & v)379 bool push_back(const value_type& v) { 380 Impl::push_back(v); 381 return true; 382 } 383 push_back(value_type && v)384 bool push_back(value_type&& v) { 385 Impl::push_back(std::move(v)); 386 return true; 387 } 388 389 using Impl::clear; 390 using Impl::pop_back; 391 unstable_erase(iterator it)392 void unstable_erase(iterator it) { 393 if (it != last()) replace(it, std::move(back())); 394 pop_back(); 395 } 396 promote()397 std::vector<T> promote() && { return std::move(*this); } 398 399 private: 400 template <typename U, std::size_t M> convert(SmallVector<U,M> && other)401 static Impl convert(SmallVector<U, M>&& other) { 402 if constexpr (std::is_constructible_v<Impl, std::vector<U>&&>) { 403 return std::move(other).promote(); 404 } else { 405 SmallVector vector(other.size()); 406 407 // Consistently with StaticVector, T only requires copy/move construction from U, rather than 408 // copy/move assignment. 409 auto it = vector.begin(); 410 for (auto& element : other) { 411 vector.replace(it++, std::move(element)); 412 } 413 414 return vector; 415 } 416 } 417 }; 418 419 template <typename> 420 struct is_small_vector : std::false_type {}; 421 422 template <typename T, std::size_t N> 423 struct is_small_vector<SmallVector<T, N>> : std::true_type {}; 424 425 // Deduction guide for array constructor. 426 template <typename T, std::size_t N> 427 SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>; 428 429 // Deduction guide for variadic constructor. 430 template <typename T, typename... Us, typename V = std::decay_t<T>, 431 typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>> 432 SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>; 433 434 // Deduction guide for in-place constructor. 435 template <typename T, std::size_t... Sizes, typename... Types> 436 SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&) 437 -> SmallVector<T, sizeof...(Sizes)>; 438 439 // Deduction guide for StaticVector conversion. 440 template <typename T, std::size_t N> 441 SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>; 442 443 template <typename T, std::size_t N> 444 inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) { 445 lhs.swap(rhs); 446 } 447 448 } // namespace android::ftl 449