1 /** 2 * Copyright (c) 2021-2024 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 LIBPANDABASE_UTILS_SMALL_VECTOR_H 17 #define LIBPANDABASE_UTILS_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(data()); 346 } begin()347 iterator begin() 348 { 349 return iterator(data()); 350 } cbegin()351 const_iterator cbegin() const 352 { 353 return const_iterator(data()); 354 } end()355 const_iterator end() const 356 { 357 return begin() + size(); 358 } end()359 iterator end() 360 { 361 return begin() + size(); 362 } cend()363 const_iterator cend() const 364 { 365 return begin() + size(); 366 } 367 rbegin()368 auto rbegin() const 369 { 370 return const_reverse_iterator(data() + size() - 1); 371 } rbegin()372 auto rbegin() 373 { 374 return reverse_iterator(data() + size() - 1); 375 } crbegin()376 auto crbegin() const 377 { 378 return const_reverse_iterator(data() + size() - 1); 379 } rend()380 auto rend() const 381 { 382 return const_reverse_iterator(data() - 1); 383 } rend()384 auto rend() 385 { 386 return reverse_iterator(data() - 1); 387 } crend()388 auto crend() const 389 { 390 return const_reverse_iterator(data() - 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 data()404 pointer data() 405 { 406 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 407 return IsStatic() ? buffer_.data.data() : vector_.data(); 408 } 409 size()410 size_t size() const 411 { 412 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 413 return IsStatic() ? buffer_.size : vector_.size(); 414 } 415 capacity()416 size_t capacity() const 417 { 418 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 419 return IsStatic() ? buffer_.data.size() : vector_.capacity(); 420 } 421 push_back(const value_type & value)422 void push_back(const value_type &value) 423 { 424 if (!EnsureStaticSpace(1)) { 425 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 426 vector_.push_back(value); 427 } else { 428 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 429 new (&buffer_.data[buffer_.size++]) T(value); 430 } 431 } 432 push_back(value_type && value)433 void push_back(value_type &&value) 434 { 435 if (!EnsureStaticSpace(1)) { 436 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 437 vector_.push_back(std::move(value)); 438 } else { 439 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 440 new (&buffer_.data[buffer_.size++]) T(std::move(value)); 441 } 442 } 443 444 template <typename... _Args> emplace_back(_Args &&...values)445 reference emplace_back(_Args &&... values) 446 { 447 if (!EnsureStaticSpace(1)) { 448 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 449 return vector_.emplace_back(std::move(values)...); 450 } 451 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 452 return *(new (&buffer_.data[buffer_.size++]) T(std::move(values)...)); 453 } 454 reserve(size_t size)455 void reserve(size_t size) 456 { 457 if (size > capacity()) { 458 if (IsStatic()) { 459 ASSERT(size > N); 460 MoveToVector(size); 461 } else { 462 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 463 vector_.reserve(size); 464 } 465 } 466 } 467 resize(size_t size)468 void resize(size_t size) 469 { 470 if (size <= this->size()) { 471 if (IsStatic()) { 472 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 473 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 474 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 475 buffer_.size = size; 476 } else { 477 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 478 vector_.resize(size); 479 } 480 } else { 481 if (EnsureStaticSpace(size - this->size())) { 482 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 483 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size, 484 [](T &v) { new (&v) T; }); 485 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 486 buffer_.size = size; 487 } else { 488 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 489 vector_.resize(size); 490 } 491 } 492 } 493 resize(size_t size,const value_type & val)494 void resize(size_t size, const value_type &val) 495 { 496 if (size <= this->size()) { 497 if (IsStatic()) { 498 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 499 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 500 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 501 buffer_.size = size; 502 } else { 503 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 504 vector_.resize(size, val); 505 } 506 } else { 507 if (EnsureStaticSpace(size - this->size())) { 508 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 509 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size, 510 [&val](T &v) { new (&v) T(val); }); 511 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 512 buffer_.size = size; 513 } else { 514 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 515 vector_.resize(size, val); 516 } 517 } 518 } 519 clear()520 void clear() 521 { 522 if (IsStatic()) { 523 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 524 std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 525 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 526 buffer_.size = 0; 527 } else { 528 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 529 vector_.clear(); 530 } 531 } 532 back()533 reference back() 534 { 535 ASSERT(size() > 0); 536 537 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 538 return IsStatic() ? buffer_.data[buffer_.size - 1] : vector_.back(); 539 } 540 541 reference operator[](size_t i) 542 { 543 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 544 return IsStatic() ? buffer_.data[i] : vector_[i]; 545 } 546 const_reference operator[](size_t i) const 547 { 548 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 549 return IsStatic() ? buffer_.data[i] : vector_[i]; 550 } 551 IsStatic()552 bool IsStatic() const 553 { 554 return (bit_cast<uintptr_t>(allocator_) & 1U) != 0; 555 } 556 557 private: EnsureStaticSpace(size_t size)558 bool EnsureStaticSpace(size_t size) 559 { 560 if (!IsStatic()) { 561 return false; 562 } 563 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 564 size_t size_required = buffer_.size + size; 565 if (size_required > N) { 566 MoveToVector(size_required); 567 return false; 568 } 569 return true; 570 } 571 MoveStaticBufferData(VectorType & tmp_vector)572 void MoveStaticBufferData(VectorType &tmp_vector) 573 { 574 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 575 for (uint32_t i = 0; i < buffer_.size; ++i) { 576 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 577 tmp_vector.emplace_back(std::move(buffer_.data[i])); 578 } 579 } 580 MoveToVector(size_t reserved_size)581 void MoveToVector(size_t reserved_size) 582 { 583 ASSERT(IsStatic()); 584 allocator_ = reinterpret_cast<Allocator *>(bit_cast<uintptr_t>(allocator_) & ~1LLU); 585 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) 586 if constexpr (use_allocator) { 587 ASSERT(allocator_ != nullptr); 588 VectorType tmp_vector(allocator_->Adapter()); 589 tmp_vector.reserve(reserved_size); 590 MoveStaticBufferData(tmp_vector); 591 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 592 new (&vector_) VectorType(std::move(tmp_vector)); 593 // NOLINTNEXTLINE(readability-misleading-indentation) 594 } else { 595 VectorType tmp_vector; 596 tmp_vector.reserve(reserved_size); 597 MoveStaticBufferData(tmp_vector); 598 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 599 new (&vector_) VectorType(std::move(tmp_vector)); 600 } 601 } 602 Destroy()603 void Destroy() 604 { 605 if (IsStatic()) { 606 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 607 std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); }); 608 } else { 609 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 610 vector_.~VectorType(); 611 } 612 } 613 ResetToStatic()614 void ResetToStatic() 615 { 616 allocator_ = AddStaticFlag(allocator_); 617 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 618 buffer_.size = 0; 619 } 620 AddStaticFlag(Allocator * p)621 static Allocator *AddStaticFlag(Allocator *p) 622 { 623 return reinterpret_cast<Allocator *>((bit_cast<uintptr_t>(p) | 1U)); 624 } 625 626 private: 627 union { 628 StaticBuffer buffer_; 629 VectorType vector_; 630 }; 631 Allocator *allocator_ {AddStaticFlag(nullptr)}; 632 }; 633 634 } // namespace panda 635 636 #endif // LIBPANDABASE_UTILS_SMALL_VECTOR_H 637