1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_SMALL_VECTOR_H 17 #define PANDA_SMALL_VECTOR_H 18 19 #include "utils/arch.h" 20 #include <algorithm> 21 #include <array> 22 #include <vector> 23 #if defined(__clang__) 24 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 25 #elif defined(__GNUC__) 26 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 27 #endif 28 29 namespace panda { 30 31 /** 32 * Helper class that provides main Panda's allocator interface: AdapterType, Adapter(), GetInstance(). 33 */ 34 class StdAllocatorStub { 35 public: 36 template <typename T> 37 using AdapterType = std::allocator<T>; 38 Adapter()39 auto Adapter() 40 { 41 // We can't std::allocator<void> because it is removed in C++20 42 return std::allocator<uint8_t>(); 43 } 44 GetInstance()45 static StdAllocatorStub *GetInstance() 46 { 47 alignas(StdAllocatorStub *) static StdAllocatorStub instance; 48 return &instance; 49 } 50 }; 51 52 template <typename Allocator, typename T, bool use_allocator> 53 class AllocatorConfig { 54 public: 55 using Adapter = typename Allocator::template AdapterType<T>; 56 }; 57 58 template <typename Allocator, typename T> 59 class AllocatorConfig<Allocator, T, false> { 60 public: 61 using Adapter = Allocator; 62 }; 63 64 /** 65 * SmallVector stores `N` elements statically inside its static buffer. Static buffer shares memory with a std::vector 66 * that will be created once number of elements exceed size of the static buffer - `N`. 67 * 68 * @tparam T Type of elements to store 69 * @tparam N Number of elements to be stored statically 70 * @tparam Allocator Allocator that will be used to allocate memory for the dynamic storage 71 * @tparam use_allocator indicates type of Allocator: 72 * false - Allocator is adapter(e.g.AllocatorAdapter) for memory allocate/deallocate/construct/destry... 73 * true - Allocator is allocator(e.g.StdAllocatorStub) that implements Adapter() returning a adapter instance 74 */ 75 template <typename T, size_t N, typename Allocator = std::allocator<T>, bool use_allocator = false> 76 class SmallVector { 77 // Least bit of the pointer should not be used in a memory addressing, because we pack `allocated` flag there 78 static_assert(alignof(Allocator *) > 1); 79 // When N is zero, then consider using std::vector directly 80 static_assert(N != 0); 81 82 struct StaticBuffer { 83 uint32_t size {0}; 84 std::array<T, N> data; 85 }; 86 87 using VectorType = std::vector<T, typename AllocatorConfig<Allocator, T, use_allocator>::Adapter>; 88 89 public: 90 using value_type = typename VectorType::value_type; 91 using reference = typename VectorType::reference; 92 using const_reference = typename VectorType::const_reference; 93 using pointer = typename VectorType::pointer; 94 using const_pointer = typename VectorType::const_pointer; 95 using difference_type = typename VectorType::difference_type; 96 97 template <typename IteratorType, bool reverse> 98 class Iterator 99 : public std::iterator<std::random_access_iterator_tag, IteratorType, int32_t, IteratorType *, IteratorType &> { Add(difference_type v)100 IteratorType *Add(difference_type v) 101 { 102 if constexpr (reverse) { // NOLINT 103 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 104 return pointer_ -= v; 105 } else { // NOLINT 106 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 107 return pointer_ += v; 108 } 109 } Sub(difference_type v)110 IteratorType *Sub(difference_type v) 111 { 112 if constexpr (reverse) { // NOLINT 113 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 114 return pointer_ + v; 115 } else { // NOLINT 116 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 117 return pointer_ - v; 118 } 119 } 120 121 public: Iterator(IteratorType * param_pointer)122 explicit Iterator(IteratorType *param_pointer) : pointer_(param_pointer) {} 123 124 IteratorType *operator->() 125 { 126 return pointer_; 127 } 128 IteratorType &operator*() 129 { 130 return *pointer_; 131 } 132 Iterator &operator++() 133 { 134 pointer_ = Add(1); 135 return *this; 136 } 137 Iterator operator++(int) // NOLINT(cert-dcl21-cpp) 138 { 139 Iterator it(pointer_); 140 pointer_ = Add(1); 141 return it; 142 } 143 Iterator &operator--() 144 { 145 pointer_ = Sub(1); 146 return *this; 147 } 148 Iterator operator--(int) // NOLINT(cert-dcl21-cpp) 149 { 150 Iterator it(pointer_); 151 pointer_ = Sub(1); 152 return it; 153 } 154 Iterator &operator+=(difference_type n) 155 { 156 pointer_ = Add(n); 157 return *this; 158 } 159 Iterator &operator-=(difference_type n) 160 { 161 pointer_ = Sub(n); 162 return *this; 163 } 164 Iterator operator+(int32_t n) const 165 { 166 Iterator it(*this); 167 it.pointer_ = it.Add(n); 168 return it; 169 } 170 Iterator operator-(int32_t n) const 171 { 172 Iterator it(*this); 173 it.pointer_ = it.Sub(n); 174 return it; 175 } 176 difference_type operator-(const Iterator &rhs) const 177 { 178 if constexpr (reverse) { // NOLINT 179 return rhs.pointer_ - pointer_; 180 } 181 return pointer_ - rhs.pointer_; 182 } 183 bool operator==(const Iterator &rhs) const 184 { 185 return pointer_ == rhs.pointer_; 186 } 187 bool operator!=(const Iterator &rhs) const 188 { 189 return !(*this == rhs); 190 } 191 192 ~Iterator() = default; 193 194 DEFAULT_COPY_SEMANTIC(Iterator); 195 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(Iterator); 196 197 private: 198 IteratorType *pointer_; 199 }; 200 201 using iterator = Iterator<T, false>; 202 using const_iterator = Iterator<const T, false>; 203 using reverse_iterator = Iterator<T, true>; 204 using const_reverse_iterator = Iterator<const T, true>; 205 SmallVector()206 SmallVector() 207 { 208 static_assert(!use_allocator); 209 210 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 211 buffer_.size = 0; 212 } 213 SmallVector(std::initializer_list<T> list)214 SmallVector(std::initializer_list<T> list) 215 { 216 static_assert(!use_allocator); 217 218 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 219 buffer_.size = 0; 220 221 for (auto it = list.begin(); it != list.end(); ++it) { 222 push_back(*it); 223 } 224 } 225 SmallVector(Allocator * allocator)226 explicit SmallVector(Allocator *allocator) : allocator_(AddStaticFlag(allocator)) 227 { 228 static_assert(use_allocator); 229 ASSERT(allocator != nullptr); 230 231 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 232 buffer_.size = 0; 233 } 234 SmallVector(const SmallVector & other)235 SmallVector(const SmallVector &other) : allocator_(other.allocator_) 236 { 237 if (other.IsStatic()) { 238 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 239 buffer_.size = other.buffer_.size; 240 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 241 for (uint32_t i = 0; i < buffer_.size; ++i) { 242 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 243 new (&buffer_.data[i]) T(other.buffer_.data[i]); 244 } 245 } else { 246 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 247 new (&vector_) VectorType(other.vector_); 248 } 249 } 250 SmallVector(SmallVector && other)251 SmallVector(SmallVector &&other) noexcept : allocator_(other.allocator_) 252 { 253 if (other.IsStatic()) { 254 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 255 buffer_.size = other.buffer_.size; 256 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 257 for (uint32_t i = 0; i < buffer_.size; ++i) { 258 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 259 new (&buffer_.data[i]) T(std::move(other.buffer_.data[i])); 260 } 261 } else { 262 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 263 new (&vector_) VectorType(std::move(other.vector_)); 264 } 265 other.ResetToStatic(); 266 } 267 ~SmallVector()268 virtual ~SmallVector() 269 { 270 Destroy(); 271 } 272 273 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment, cert-oop54-cpp) 274 SmallVector &operator=(const SmallVector &other) 275 { 276 if (&other == this) { 277 return *this; 278 } 279 280 Destroy(); 281 allocator_ = other.allocator_; 282 if (other.IsStatic()) { 283 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 284 buffer_.size = other.buffer_.size; 285 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 286 for (uint32_t i = 0; i < buffer_.size; ++i) { 287 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 288 new (&buffer_.data[i]) T(other.buffer_.data[i]); 289 } 290 } else { 291 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 292 new (&vector_) VectorType(other.vector_); 293 } 294 295 return *this; 296 } 297 298 SmallVector &operator=(SmallVector &&other) noexcept 299 { 300 Destroy(); 301 allocator_ = other.allocator_; 302 if (other.IsStatic()) { 303 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 304 buffer_.size = other.buffer_.size; 305 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 306 for (uint32_t i = 0; i < buffer_.size; ++i) { 307 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 308 new (&buffer_.data[i]) T(std::move(other.buffer_.data[i])); 309 } 310 } else { 311 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 312 new (&vector_) VectorType(std::move(other.vector_)); 313 } 314 other.ResetToStatic(); 315 return *this; 316 } 317 318 bool operator==(const SmallVector &other) const 319 { 320 if (this == &other) { 321 return true; 322 } 323 324 if (size() != other.size()) { 325 return false; 326 } 327 328 auto it1 = begin(); 329 auto it2 = other.begin(); 330 for (; it1 != end(); ++it1, ++it2) { 331 if (*it1 != *it2) { 332 return false; 333 } 334 } 335 return true; 336 } 337 338 bool operator!=(const SmallVector &other) const 339 { 340 return !operator==(other); 341 } 342 begin()343 const_iterator begin() const 344 { 345 return const_iterator(&operator[](0)); 346 } begin()347 iterator begin() 348 { 349 return iterator(&operator[](0)); 350 } cbegin()351 const_iterator cbegin() const 352 { 353 return const_iterator(&operator[](0)); 354 } end()355 const_iterator end() const 356 { 357 return const_iterator(&operator[](size())); 358 } end()359 iterator end() 360 { 361 return iterator(&operator[](size())); 362 } cend()363 const_iterator cend() const 364 { 365 return const_iterator(&operator[](size())); 366 } 367 rbegin()368 auto rbegin() const 369 { 370 return const_reverse_iterator(&operator[](size() - 1)); 371 } rbegin()372 auto rbegin() 373 { 374 return reverse_iterator(&operator[](size() - 1)); 375 } crbegin()376 auto crbegin() const 377 { 378 return const_reverse_iterator(&operator[](size() - 1)); 379 } rend()380 auto rend() const 381 { 382 return const_reverse_iterator(&operator[](0) - 1); 383 } rend()384 auto rend() 385 { 386 return reverse_iterator(&operator[](0) - 1); 387 } crend()388 auto crend() const 389 { 390 return const_reverse_iterator(&operator[](0) - 1); 391 } 392 empty()393 bool empty() const 394 { 395 return size() == 0; 396 } 397 data()398 const_pointer data() const 399 { 400 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 401 return IsStatic() ? buffer_.data.data() : vector_.data(); 402 } 403 size()404 size_t size() const 405 { 406 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 407 return IsStatic() ? buffer_.size : vector_.size(); 408 } 409 capacity()410 size_t capacity() const 411 { 412 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 413 return IsStatic() ? buffer_.data.size() : vector_.capacity(); 414 } 415 push_back(const value_type & value)416 void push_back(const value_type &value) 417 { 418 if (!EnsureStaticSpace(1)) { 419 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 420 vector_.push_back(value); 421 } else { 422 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 423 new (&buffer_.data[buffer_.size++]) T(value); 424 } 425 } 426 push_back(value_type && value)427 void push_back(value_type &&value) 428 { 429 if (!EnsureStaticSpace(1)) { 430 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 431 vector_.push_back(std::move(value)); 432 } else { 433 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 434 new (&buffer_.data[buffer_.size++]) T(std::move(value)); 435 } 436 } 437 438 template <typename... _Args> emplace_back(_Args &&...values)439 reference emplace_back(_Args &&... values) 440 { 441 if (!EnsureStaticSpace(1)) { 442 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 443 return vector_.emplace_back(std::move(values)...); 444 } 445 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 446 return *(new (&buffer_.data[buffer_.size++]) T(std::move(values)...)); 447 } 448 reserve(size_t size)449 void reserve(size_t size) 450 { 451 if (size > capacity()) { 452 if (IsStatic()) { 453 ASSERT(size > N); 454 MoveToVector(size); 455 } else { 456 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 457 vector_.reserve(size); 458 } 459 } 460 } 461 resize(size_t size)462 void resize(size_t size) 463 { 464 if (size <= this->size()) { 465 if (IsStatic()) { 466 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 467 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 468 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 469 buffer_.size = size; 470 } else { 471 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 472 vector_.resize(size); 473 } 474 } else { 475 if (EnsureStaticSpace(size - this->size())) { 476 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 477 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size, 478 [](T &v) { new (&v) T; }); 479 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 480 buffer_.size = size; 481 } else { 482 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 483 vector_.resize(size); 484 } 485 } 486 } 487 resize(size_t size,const value_type & val)488 void resize(size_t size, const value_type &val) 489 { 490 if (size <= this->size()) { 491 if (IsStatic()) { 492 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 493 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 494 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 495 buffer_.size = size; 496 } else { 497 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 498 vector_.resize(size, val); 499 } 500 } else { 501 if (EnsureStaticSpace(size - this->size())) { 502 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 503 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size, 504 [&val](T &v) { new (&v) T(val); }); 505 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 506 buffer_.size = size; 507 } else { 508 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 509 vector_.resize(size, val); 510 } 511 } 512 } 513 clear()514 void clear() 515 { 516 if (IsStatic()) { 517 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 518 std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 519 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 520 buffer_.size = 0; 521 } else { 522 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 523 vector_.clear(); 524 } 525 } 526 back()527 reference back() 528 { 529 ASSERT(size() > 0); 530 531 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 532 return IsStatic() ? buffer_.data[buffer_.size - 1] : vector_.back(); 533 } 534 535 reference operator[](size_t i) 536 { 537 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 538 return IsStatic() ? buffer_.data[i] : vector_[i]; 539 } 540 const_reference operator[](size_t i) const 541 { 542 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 543 return IsStatic() ? buffer_.data[i] : vector_[i]; 544 } 545 IsStatic()546 bool IsStatic() const 547 { 548 return (bit_cast<uintptr_t>(allocator_) & 1U) != 0; 549 } 550 551 private: EnsureStaticSpace(size_t size)552 bool EnsureStaticSpace(size_t size) 553 { 554 if (!IsStatic()) { 555 return false; 556 } 557 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 558 size_t size_required = buffer_.size + size; 559 if (size_required > N) { 560 MoveToVector(size_required); 561 return false; 562 } 563 return true; 564 } 565 MoveStaticBufferData(VectorType & tmp_vector)566 void MoveStaticBufferData(VectorType &tmp_vector) 567 { 568 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 569 for (uint32_t i = 0; i < buffer_.size; ++i) { 570 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 571 tmp_vector.emplace_back(std::move(buffer_.data[i])); 572 } 573 } 574 MoveToVector(size_t reserved_size)575 void MoveToVector(size_t reserved_size) 576 { 577 ASSERT(IsStatic()); 578 allocator_ = reinterpret_cast<Allocator *>(bit_cast<uintptr_t>(allocator_) & ~1LLU); 579 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) 580 if constexpr (use_allocator) { 581 ASSERT(allocator_ != nullptr); 582 VectorType tmp_vector(allocator_->Adapter()); 583 tmp_vector.reserve(reserved_size); 584 MoveStaticBufferData(tmp_vector); 585 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 586 new (&vector_) VectorType(std::move(tmp_vector)); 587 // NOLINTNEXTLINE(readability-misleading-indentation) 588 } else { 589 VectorType tmp_vector; 590 tmp_vector.reserve(reserved_size); 591 MoveStaticBufferData(tmp_vector); 592 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 593 new (&vector_) VectorType(std::move(tmp_vector)); 594 } 595 } 596 Destroy()597 void Destroy() 598 { 599 if (IsStatic()) { 600 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 601 std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 602 } else { 603 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 604 vector_.~VectorType(); 605 } 606 } 607 ResetToStatic()608 void ResetToStatic() 609 { 610 allocator_ = AddStaticFlag(allocator_); 611 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 612 buffer_.size = 0; 613 } 614 AddStaticFlag(Allocator * p)615 static Allocator *AddStaticFlag(Allocator *p) 616 { 617 return reinterpret_cast<Allocator *>((bit_cast<uintptr_t>(p) | 1U)); 618 } 619 620 private: 621 union { 622 StaticBuffer buffer_; 623 VectorType vector_; 624 }; 625 Allocator *allocator_ {AddStaticFlag(nullptr)}; 626 }; 627 628 } // namespace panda 629 630 #endif // PANDA_SMALL_VECTOR_H 631