fixed_array.h (20278B)
1 // Copyright 2018 The Abseil Authors. 2 // 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 // https://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 // File: fixed_array.h 17 // ----------------------------------------------------------------------------- 18 // 19 // A `FixedArray<T>` represents a non-resizable array of `T` where the length of 20 // the array can be determined at run-time. It is a good replacement for 21 // non-standard and deprecated uses of `alloca()` and variable length arrays 22 // within the GCC extension. (See 23 // https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html). 24 // 25 // `FixedArray` allocates small arrays inline, keeping performance fast by 26 // avoiding heap operations. It also helps reduce the chances of 27 // accidentally overflowing your stack if large input is passed to 28 // your function. 29 30 #ifndef ABSL_CONTAINER_FIXED_ARRAY_H_ 31 #define ABSL_CONTAINER_FIXED_ARRAY_H_ 32 33 #include <algorithm> 34 #include <cassert> 35 #include <cstddef> 36 #include <initializer_list> 37 #include <iterator> 38 #include <limits> 39 #include <memory> 40 #include <new> 41 #include <type_traits> 42 43 #include "absl/algorithm/algorithm.h" 44 #include "absl/base/attributes.h" 45 #include "absl/base/config.h" 46 #include "absl/base/dynamic_annotations.h" 47 #include "absl/base/internal/iterator_traits.h" 48 #include "absl/base/internal/throw_delegate.h" 49 #include "absl/base/macros.h" 50 #include "absl/base/optimization.h" 51 #include "absl/base/port.h" 52 #include "absl/container/internal/compressed_tuple.h" 53 #include "absl/memory/memory.h" 54 55 namespace absl { 56 ABSL_NAMESPACE_BEGIN 57 58 constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); 59 60 // ----------------------------------------------------------------------------- 61 // FixedArray 62 // ----------------------------------------------------------------------------- 63 // 64 // A `FixedArray` provides a run-time fixed-size array, allocating a small array 65 // inline for efficiency. 66 // 67 // Most users should not specify the `N` template parameter and let `FixedArray` 68 // automatically determine the number of elements to store inline based on 69 // `sizeof(T)`. If `N` is specified, the `FixedArray` implementation will use 70 // inline storage for arrays with a length <= `N`. 71 // 72 // Note that a `FixedArray` constructed with a `size_type` argument will 73 // default-initialize its values by leaving trivially constructible types 74 // uninitialized (e.g. int, int[4], double), and others default-constructed. 75 // This matches the behavior of c-style arrays and `std::array`, but not 76 // `std::vector`. 77 template <typename T, size_t N = kFixedArrayUseDefault, 78 typename A = std::allocator<T>> 79 class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray { 80 static_assert(!std::is_array<T>::value || std::extent<T>::value > 0, 81 "Arrays with unknown bounds cannot be used with FixedArray."); 82 83 static constexpr size_t kInlineBytesDefault = 256; 84 85 using AllocatorTraits = std::allocator_traits<A>; 86 // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, 87 // but this seems to be mostly pedantic. 88 template <typename Iterator> 89 using EnableIfForwardIterator = std::enable_if_t< 90 base_internal::IsAtLeastForwardIterator<Iterator>::value>; 91 static constexpr bool NoexceptCopyable() { 92 return std::is_nothrow_copy_constructible<StorageElement>::value && 93 absl::allocator_is_nothrow<allocator_type>::value; 94 } 95 static constexpr bool NoexceptMovable() { 96 return std::is_nothrow_move_constructible<StorageElement>::value && 97 absl::allocator_is_nothrow<allocator_type>::value; 98 } 99 static constexpr bool DefaultConstructorIsNonTrivial() { 100 return !absl::is_trivially_default_constructible<StorageElement>::value; 101 } 102 103 public: 104 using allocator_type = typename AllocatorTraits::allocator_type; 105 using value_type = typename AllocatorTraits::value_type; 106 using pointer = typename AllocatorTraits::pointer; 107 using const_pointer = typename AllocatorTraits::const_pointer; 108 using reference = value_type&; 109 using const_reference = const value_type&; 110 using size_type = typename AllocatorTraits::size_type; 111 using difference_type = typename AllocatorTraits::difference_type; 112 using iterator = pointer; 113 using const_iterator = const_pointer; 114 using reverse_iterator = std::reverse_iterator<iterator>; 115 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 116 117 static constexpr size_type inline_elements = 118 (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) 119 : static_cast<size_type>(N)); 120 121 FixedArray(const FixedArray& other) noexcept(NoexceptCopyable()) 122 : FixedArray(other, 123 AllocatorTraits::select_on_container_copy_construction( 124 other.storage_.alloc())) {} 125 126 FixedArray(const FixedArray& other, 127 const allocator_type& a) noexcept(NoexceptCopyable()) 128 : FixedArray(other.begin(), other.end(), a) {} 129 130 FixedArray(FixedArray&& other) noexcept(NoexceptMovable()) 131 : FixedArray(std::move(other), other.storage_.alloc()) {} 132 133 FixedArray(FixedArray&& other, 134 const allocator_type& a) noexcept(NoexceptMovable()) 135 : FixedArray(std::make_move_iterator(other.begin()), 136 std::make_move_iterator(other.end()), a) {} 137 138 // Creates an array object that can store `n` elements. 139 // Note that trivially constructible elements will be uninitialized. 140 explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) 141 : storage_(n, a) { 142 if (DefaultConstructorIsNonTrivial()) { 143 memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), 144 storage_.end()); 145 } 146 } 147 148 // Creates an array initialized with `n` copies of `val`. 149 FixedArray(size_type n, const value_type& val, 150 const allocator_type& a = allocator_type()) 151 : storage_(n, a) { 152 memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), 153 storage_.end(), val); 154 } 155 156 // Creates an array initialized with the size and contents of `init_list`. 157 FixedArray(std::initializer_list<value_type> init_list, 158 const allocator_type& a = allocator_type()) 159 : FixedArray(init_list.begin(), init_list.end(), a) {} 160 161 // Creates an array initialized with the elements from the input 162 // range. The array's size will always be `std::distance(first, last)`. 163 // REQUIRES: Iterator must be a forward_iterator or better. 164 template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr> 165 FixedArray(Iterator first, Iterator last, 166 const allocator_type& a = allocator_type()) 167 : storage_(std::distance(first, last), a) { 168 memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); 169 } 170 171 ~FixedArray() noexcept { 172 for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { 173 AllocatorTraits::destroy(storage_.alloc(), cur); 174 } 175 } 176 177 // Assignments are deleted because they break the invariant that the size of a 178 // `FixedArray` never changes. 179 void operator=(FixedArray&&) = delete; 180 void operator=(const FixedArray&) = delete; 181 182 // FixedArray::size() 183 // 184 // Returns the length of the fixed array. 185 size_type size() const { return storage_.size(); } 186 187 // FixedArray::max_size() 188 // 189 // Returns the largest possible value of `std::distance(begin(), end())` for a 190 // `FixedArray<T>`. This is equivalent to the most possible addressable bytes 191 // over the number of bytes taken by T. 192 constexpr size_type max_size() const { 193 return (std::numeric_limits<difference_type>::max)() / sizeof(value_type); 194 } 195 196 // FixedArray::empty() 197 // 198 // Returns whether or not the fixed array is empty. 199 bool empty() const { return size() == 0; } 200 201 // FixedArray::memsize() 202 // 203 // Returns the memory size of the fixed array in bytes. 204 size_t memsize() const { return size() * sizeof(value_type); } 205 206 // FixedArray::data() 207 // 208 // Returns a const T* pointer to elements of the `FixedArray`. This pointer 209 // can be used to access (but not modify) the contained elements. 210 const_pointer data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 211 return AsValueType(storage_.begin()); 212 } 213 214 // Overload of FixedArray::data() to return a T* pointer to elements of the 215 // fixed array. This pointer can be used to access and modify the contained 216 // elements. 217 pointer data() ABSL_ATTRIBUTE_LIFETIME_BOUND { 218 return AsValueType(storage_.begin()); 219 } 220 221 // FixedArray::operator[] 222 // 223 // Returns a reference the ith element of the fixed array. 224 // REQUIRES: 0 <= i < size() 225 reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { 226 ABSL_HARDENING_ASSERT(i < size()); 227 return data()[i]; 228 } 229 230 // Overload of FixedArray::operator()[] to return a const reference to the 231 // ith element of the fixed array. 232 // REQUIRES: 0 <= i < size() 233 const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { 234 ABSL_HARDENING_ASSERT(i < size()); 235 return data()[i]; 236 } 237 238 // FixedArray::at 239 // 240 // Bounds-checked access. Returns a reference to the ith element of the fixed 241 // array, or throws std::out_of_range 242 reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND { 243 if (ABSL_PREDICT_FALSE(i >= size())) { 244 base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); 245 } 246 return data()[i]; 247 } 248 249 // Overload of FixedArray::at() to return a const reference to the ith element 250 // of the fixed array. 251 const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND { 252 if (ABSL_PREDICT_FALSE(i >= size())) { 253 base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); 254 } 255 return data()[i]; 256 } 257 258 // FixedArray::front() 259 // 260 // Returns a reference to the first element of the fixed array. 261 reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND { 262 ABSL_HARDENING_ASSERT(!empty()); 263 return data()[0]; 264 } 265 266 // Overload of FixedArray::front() to return a reference to the first element 267 // of a fixed array of const values. 268 const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 269 ABSL_HARDENING_ASSERT(!empty()); 270 return data()[0]; 271 } 272 273 // FixedArray::back() 274 // 275 // Returns a reference to the last element of the fixed array. 276 reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND { 277 ABSL_HARDENING_ASSERT(!empty()); 278 return data()[size() - 1]; 279 } 280 281 // Overload of FixedArray::back() to return a reference to the last element 282 // of a fixed array of const values. 283 const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 284 ABSL_HARDENING_ASSERT(!empty()); 285 return data()[size() - 1]; 286 } 287 288 // FixedArray::begin() 289 // 290 // Returns an iterator to the beginning of the fixed array. 291 iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); } 292 293 // Overload of FixedArray::begin() to return a const iterator to the 294 // beginning of the fixed array. 295 const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); } 296 297 // FixedArray::cbegin() 298 // 299 // Returns a const iterator to the beginning of the fixed array. 300 const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 301 return begin(); 302 } 303 304 // FixedArray::end() 305 // 306 // Returns an iterator to the end of the fixed array. 307 iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data() + size(); } 308 309 // Overload of FixedArray::end() to return a const iterator to the end of the 310 // fixed array. 311 const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 312 return data() + size(); 313 } 314 315 // FixedArray::cend() 316 // 317 // Returns a const iterator to the end of the fixed array. 318 const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); } 319 320 // FixedArray::rbegin() 321 // 322 // Returns a reverse iterator from the end of the fixed array. 323 reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND { 324 return reverse_iterator(end()); 325 } 326 327 // Overload of FixedArray::rbegin() to return a const reverse iterator from 328 // the end of the fixed array. 329 const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 330 return const_reverse_iterator(end()); 331 } 332 333 // FixedArray::crbegin() 334 // 335 // Returns a const reverse iterator from the end of the fixed array. 336 const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 337 return rbegin(); 338 } 339 340 // FixedArray::rend() 341 // 342 // Returns a reverse iterator from the beginning of the fixed array. 343 reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND { 344 return reverse_iterator(begin()); 345 } 346 347 // Overload of FixedArray::rend() for returning a const reverse iterator 348 // from the beginning of the fixed array. 349 const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 350 return const_reverse_iterator(begin()); 351 } 352 353 // FixedArray::crend() 354 // 355 // Returns a reverse iterator from the beginning of the fixed array. 356 const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { 357 return rend(); 358 } 359 360 // FixedArray::fill() 361 // 362 // Assigns the given `value` to all elements in the fixed array. 363 void fill(const value_type& val) { std::fill(begin(), end(), val); } 364 365 // Relational operators. Equality operators are elementwise using 366 // `operator==`, while order operators order FixedArrays lexicographically. 367 friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { 368 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 369 } 370 371 friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { 372 return !(lhs == rhs); 373 } 374 375 friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { 376 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), 377 rhs.end()); 378 } 379 380 friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { 381 return rhs < lhs; 382 } 383 384 friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { 385 return !(rhs < lhs); 386 } 387 388 friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { 389 return !(lhs < rhs); 390 } 391 392 template <typename H> 393 friend H AbslHashValue(H h, const FixedArray& v) { 394 return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), 395 v.size()); 396 } 397 398 private: 399 // StorageElement 400 // 401 // For FixedArrays with a C-style-array value_type, StorageElement is a POD 402 // wrapper struct called StorageElementWrapper that holds the value_type 403 // instance inside. This is needed for construction and destruction of the 404 // entire array regardless of how many dimensions it has. For all other cases, 405 // StorageElement is just an alias of value_type. 406 // 407 // Maintainer's Note: The simpler solution would be to simply wrap value_type 408 // in a struct whether it's an array or not. That causes some paranoid 409 // diagnostics to misfire, believing that 'data()' returns a pointer to a 410 // single element, rather than the packed array that it really is. 411 // e.g.: 412 // 413 // FixedArray<char> buf(1); 414 // sprintf(buf.data(), "foo"); 415 // 416 // error: call to int __builtin___sprintf_chk(etc...) 417 // will always overflow destination buffer [-Werror] 418 // 419 template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>, 420 size_t InnerN = std::extent<OuterT>::value> 421 struct StorageElementWrapper { 422 InnerT array[InnerN]; 423 }; 424 425 using StorageElement = 426 absl::conditional_t<std::is_array<value_type>::value, 427 StorageElementWrapper<value_type>, value_type>; 428 429 static pointer AsValueType(pointer ptr) { return ptr; } 430 static pointer AsValueType(StorageElementWrapper<value_type>* ptr) { 431 return std::addressof(ptr->array); 432 } 433 434 static_assert(sizeof(StorageElement) == sizeof(value_type), ""); 435 static_assert(alignof(StorageElement) == alignof(value_type), ""); 436 437 class NonEmptyInlinedStorage { 438 public: 439 StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); } 440 void AnnotateConstruct(size_type n); 441 void AnnotateDestruct(size_type n); 442 443 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 444 void* RedzoneBegin() { return &redzone_begin_; } 445 void* RedzoneEnd() { return &redzone_end_ + 1; } 446 #endif // ABSL_HAVE_ADDRESS_SANITIZER 447 448 private: 449 ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_); 450 alignas(StorageElement) unsigned char buff_[sizeof( 451 StorageElement[inline_elements])]; 452 ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_); 453 }; 454 455 class EmptyInlinedStorage { 456 public: 457 StorageElement* data() { return nullptr; } 458 void AnnotateConstruct(size_type) {} 459 void AnnotateDestruct(size_type) {} 460 }; 461 462 using InlinedStorage = 463 absl::conditional_t<inline_elements == 0, EmptyInlinedStorage, 464 NonEmptyInlinedStorage>; 465 466 // Storage 467 // 468 // An instance of Storage manages the inline and out-of-line memory for 469 // instances of FixedArray. This guarantees that even when construction of 470 // individual elements fails in the FixedArray constructor body, the 471 // destructor for Storage will still be called and out-of-line memory will be 472 // properly deallocated. 473 // 474 class Storage : public InlinedStorage { 475 public: 476 Storage(size_type n, const allocator_type& a) 477 : size_alloc_(n, a), data_(InitializeData()) {} 478 479 ~Storage() noexcept { 480 if (UsingInlinedStorage(size())) { 481 InlinedStorage::AnnotateDestruct(size()); 482 } else { 483 AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); 484 } 485 } 486 487 size_type size() const { return size_alloc_.template get<0>(); } 488 StorageElement* begin() const { return data_; } 489 StorageElement* end() const { return begin() + size(); } 490 allocator_type& alloc() { return size_alloc_.template get<1>(); } 491 const allocator_type& alloc() const { 492 return size_alloc_.template get<1>(); 493 } 494 495 private: 496 static bool UsingInlinedStorage(size_type n) { 497 return n <= inline_elements; 498 } 499 500 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 501 ABSL_ATTRIBUTE_NOINLINE 502 #endif // ABSL_HAVE_ADDRESS_SANITIZER 503 StorageElement* InitializeData() { 504 if (UsingInlinedStorage(size())) { 505 InlinedStorage::AnnotateConstruct(size()); 506 return InlinedStorage::data(); 507 } else { 508 return reinterpret_cast<StorageElement*>( 509 AllocatorTraits::allocate(alloc(), size())); 510 } 511 } 512 513 // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s 514 container_internal::CompressedTuple<size_type, allocator_type> size_alloc_; 515 StorageElement* data_; 516 }; 517 518 Storage storage_; 519 }; 520 521 template <typename T, size_t N, typename A> 522 void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct( 523 typename FixedArray<T, N, A>::size_type n) { 524 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 525 if (!n) return; 526 ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), 527 data() + n); 528 ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), 529 RedzoneBegin()); 530 #endif // ABSL_HAVE_ADDRESS_SANITIZER 531 static_cast<void>(n); // Mark used when not in asan mode 532 } 533 534 template <typename T, size_t N, typename A> 535 void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct( 536 typename FixedArray<T, N, A>::size_type n) { 537 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 538 if (!n) return; 539 ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, 540 RedzoneEnd()); 541 ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), 542 data()); 543 #endif // ABSL_HAVE_ADDRESS_SANITIZER 544 static_cast<void>(n); // Mark used when not in asan mode 545 } 546 ABSL_NAMESPACE_END 547 } // namespace absl 548 549 #endif // ABSL_CONTAINER_FIXED_ARRAY_H_