fixed_array.h
Go to the documentation of this file.
00001 // Copyright 2018 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 //
00015 // -----------------------------------------------------------------------------
00016 // File: fixed_array.h
00017 // -----------------------------------------------------------------------------
00018 //
00019 // A `FixedArray<T>` represents a non-resizable array of `T` where the length of
00020 // the array can be determined at run-time. It is a good replacement for
00021 // non-standard and deprecated uses of `alloca()` and variable length arrays
00022 // within the GCC extension. (See
00023 // https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
00024 //
00025 // `FixedArray` allocates small arrays inline, keeping performance fast by
00026 // avoiding heap operations. It also helps reduce the chances of
00027 // accidentally overflowing your stack if large input is passed to
00028 // your function.
00029 
00030 #ifndef ABSL_CONTAINER_FIXED_ARRAY_H_
00031 #define ABSL_CONTAINER_FIXED_ARRAY_H_
00032 
00033 #include <algorithm>
00034 #include <array>
00035 #include <cassert>
00036 #include <cstddef>
00037 #include <initializer_list>
00038 #include <iterator>
00039 #include <limits>
00040 #include <memory>
00041 #include <new>
00042 #include <type_traits>
00043 
00044 #include "absl/algorithm/algorithm.h"
00045 #include "absl/base/dynamic_annotations.h"
00046 #include "absl/base/internal/throw_delegate.h"
00047 #include "absl/base/macros.h"
00048 #include "absl/base/optimization.h"
00049 #include "absl/base/port.h"
00050 #include "absl/container/internal/compressed_tuple.h"
00051 #include "absl/memory/memory.h"
00052 
00053 namespace absl {
00054 
00055 constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
00056 
00057 // -----------------------------------------------------------------------------
00058 // FixedArray
00059 // -----------------------------------------------------------------------------
00060 //
00061 // A `FixedArray` provides a run-time fixed-size array, allocating a small array
00062 // inline for efficiency.
00063 //
00064 // Most users should not specify an `inline_elements` argument and let
00065 // `FixedArray` automatically determine the number of elements
00066 // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
00067 // `FixedArray` implementation will use inline storage for arrays with a
00068 // length <= `inline_elements`.
00069 //
00070 // Note that a `FixedArray` constructed with a `size_type` argument will
00071 // default-initialize its values by leaving trivially constructible types
00072 // uninitialized (e.g. int, int[4], double), and others default-constructed.
00073 // This matches the behavior of c-style arrays and `std::array`, but not
00074 // `std::vector`.
00075 //
00076 // Note that `FixedArray` does not provide a public allocator; if it requires a
00077 // heap allocation, it will do so with global `::operator new[]()` and
00078 // `::operator delete[]()`, even if T provides class-scope overrides for these
00079 // operators.
00080 template <typename T, size_t N = kFixedArrayUseDefault,
00081           typename A = std::allocator<T>>
00082 class FixedArray {
00083   static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
00084                 "Arrays with unknown bounds cannot be used with FixedArray.");
00085 
00086   static constexpr size_t kInlineBytesDefault = 256;
00087 
00088   using AllocatorTraits = std::allocator_traits<A>;
00089   // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
00090   // but this seems to be mostly pedantic.
00091   template <typename Iterator>
00092   using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
00093       typename std::iterator_traits<Iterator>::iterator_category,
00094       std::forward_iterator_tag>::value>;
00095   static constexpr bool NoexceptCopyable() {
00096     return std::is_nothrow_copy_constructible<StorageElement>::value &&
00097            absl::allocator_is_nothrow<allocator_type>::value;
00098   }
00099   static constexpr bool NoexceptMovable() {
00100     return std::is_nothrow_move_constructible<StorageElement>::value &&
00101            absl::allocator_is_nothrow<allocator_type>::value;
00102   }
00103   static constexpr bool DefaultConstructorIsNonTrivial() {
00104     return !absl::is_trivially_default_constructible<StorageElement>::value;
00105   }
00106 
00107  public:
00108   using allocator_type = typename AllocatorTraits::allocator_type;
00109   using value_type = typename allocator_type::value_type;
00110   using pointer = typename allocator_type::pointer;
00111   using const_pointer = typename allocator_type::const_pointer;
00112   using reference = typename allocator_type::reference;
00113   using const_reference = typename allocator_type::const_reference;
00114   using size_type = typename allocator_type::size_type;
00115   using difference_type = typename allocator_type::difference_type;
00116   using iterator = pointer;
00117   using const_iterator = const_pointer;
00118   using reverse_iterator = std::reverse_iterator<iterator>;
00119   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
00120 
00121   static constexpr size_type inline_elements =
00122       (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
00123                                   : static_cast<size_type>(N));
00124 
00125   FixedArray(
00126       const FixedArray& other,
00127       const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable())
00128       : FixedArray(other.begin(), other.end(), a) {}
00129 
00130   FixedArray(
00131       FixedArray&& other,
00132       const allocator_type& a = allocator_type()) noexcept(NoexceptMovable())
00133       : FixedArray(std::make_move_iterator(other.begin()),
00134                    std::make_move_iterator(other.end()), a) {}
00135 
00136   // Creates an array object that can store `n` elements.
00137   // Note that trivially constructible elements will be uninitialized.
00138   explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
00139       : storage_(n, a) {
00140     if (DefaultConstructorIsNonTrivial()) {
00141       memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
00142                                       storage_.end());
00143     }
00144   }
00145 
00146   // Creates an array initialized with `n` copies of `val`.
00147   FixedArray(size_type n, const value_type& val,
00148              const allocator_type& a = allocator_type())
00149       : storage_(n, a) {
00150     memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
00151                                     storage_.end(), val);
00152   }
00153 
00154   // Creates an array initialized with the size and contents of `init_list`.
00155   FixedArray(std::initializer_list<value_type> init_list,
00156              const allocator_type& a = allocator_type())
00157       : FixedArray(init_list.begin(), init_list.end(), a) {}
00158 
00159   // Creates an array initialized with the elements from the input
00160   // range. The array's size will always be `std::distance(first, last)`.
00161   // REQUIRES: Iterator must be a forward_iterator or better.
00162   template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
00163   FixedArray(Iterator first, Iterator last,
00164              const allocator_type& a = allocator_type())
00165       : storage_(std::distance(first, last), a) {
00166     memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last);
00167   }
00168 
00169   ~FixedArray() noexcept {
00170     for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
00171       AllocatorTraits::destroy(storage_.alloc(), cur);
00172     }
00173   }
00174 
00175   // Assignments are deleted because they break the invariant that the size of a
00176   // `FixedArray` never changes.
00177   void operator=(FixedArray&&) = delete;
00178   void operator=(const FixedArray&) = delete;
00179 
00180   // FixedArray::size()
00181   //
00182   // Returns the length of the fixed array.
00183   size_type size() const { return storage_.size(); }
00184 
00185   // FixedArray::max_size()
00186   //
00187   // Returns the largest possible value of `std::distance(begin(), end())` for a
00188   // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
00189   // over the number of bytes taken by T.
00190   constexpr size_type max_size() const {
00191     return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
00192   }
00193 
00194   // FixedArray::empty()
00195   //
00196   // Returns whether or not the fixed array is empty.
00197   bool empty() const { return size() == 0; }
00198 
00199   // FixedArray::memsize()
00200   //
00201   // Returns the memory size of the fixed array in bytes.
00202   size_t memsize() const { return size() * sizeof(value_type); }
00203 
00204   // FixedArray::data()
00205   //
00206   // Returns a const T* pointer to elements of the `FixedArray`. This pointer
00207   // can be used to access (but not modify) the contained elements.
00208   const_pointer data() const { return AsValueType(storage_.begin()); }
00209 
00210   // Overload of FixedArray::data() to return a T* pointer to elements of the
00211   // fixed array. This pointer can be used to access and modify the contained
00212   // elements.
00213   pointer data() { return AsValueType(storage_.begin()); }
00214 
00215   // FixedArray::operator[]
00216   //
00217   // Returns a reference the ith element of the fixed array.
00218   // REQUIRES: 0 <= i < size()
00219   reference operator[](size_type i) {
00220     assert(i < size());
00221     return data()[i];
00222   }
00223 
00224   // Overload of FixedArray::operator()[] to return a const reference to the
00225   // ith element of the fixed array.
00226   // REQUIRES: 0 <= i < size()
00227   const_reference operator[](size_type i) const {
00228     assert(i < size());
00229     return data()[i];
00230   }
00231 
00232   // FixedArray::at
00233   //
00234   // Bounds-checked access.  Returns a reference to the ith element of the
00235   // fiexed array, or throws std::out_of_range
00236   reference at(size_type i) {
00237     if (ABSL_PREDICT_FALSE(i >= size())) {
00238       base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
00239     }
00240     return data()[i];
00241   }
00242 
00243   // Overload of FixedArray::at() to return a const reference to the ith element
00244   // of the fixed array.
00245   const_reference at(size_type i) const {
00246     if (ABSL_PREDICT_FALSE(i >= size())) {
00247       base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
00248     }
00249     return data()[i];
00250   }
00251 
00252   // FixedArray::front()
00253   //
00254   // Returns a reference to the first element of the fixed array.
00255   reference front() { return *begin(); }
00256 
00257   // Overload of FixedArray::front() to return a reference to the first element
00258   // of a fixed array of const values.
00259   const_reference front() const { return *begin(); }
00260 
00261   // FixedArray::back()
00262   //
00263   // Returns a reference to the last element of the fixed array.
00264   reference back() { return *(end() - 1); }
00265 
00266   // Overload of FixedArray::back() to return a reference to the last element
00267   // of a fixed array of const values.
00268   const_reference back() const { return *(end() - 1); }
00269 
00270   // FixedArray::begin()
00271   //
00272   // Returns an iterator to the beginning of the fixed array.
00273   iterator begin() { return data(); }
00274 
00275   // Overload of FixedArray::begin() to return a const iterator to the
00276   // beginning of the fixed array.
00277   const_iterator begin() const { return data(); }
00278 
00279   // FixedArray::cbegin()
00280   //
00281   // Returns a const iterator to the beginning of the fixed array.
00282   const_iterator cbegin() const { return begin(); }
00283 
00284   // FixedArray::end()
00285   //
00286   // Returns an iterator to the end of the fixed array.
00287   iterator end() { return data() + size(); }
00288 
00289   // Overload of FixedArray::end() to return a const iterator to the end of the
00290   // fixed array.
00291   const_iterator end() const { return data() + size(); }
00292 
00293   // FixedArray::cend()
00294   //
00295   // Returns a const iterator to the end of the fixed array.
00296   const_iterator cend() const { return end(); }
00297 
00298   // FixedArray::rbegin()
00299   //
00300   // Returns a reverse iterator from the end of the fixed array.
00301   reverse_iterator rbegin() { return reverse_iterator(end()); }
00302 
00303   // Overload of FixedArray::rbegin() to return a const reverse iterator from
00304   // the end of the fixed array.
00305   const_reverse_iterator rbegin() const {
00306     return const_reverse_iterator(end());
00307   }
00308 
00309   // FixedArray::crbegin()
00310   //
00311   // Returns a const reverse iterator from the end of the fixed array.
00312   const_reverse_iterator crbegin() const { return rbegin(); }
00313 
00314   // FixedArray::rend()
00315   //
00316   // Returns a reverse iterator from the beginning of the fixed array.
00317   reverse_iterator rend() { return reverse_iterator(begin()); }
00318 
00319   // Overload of FixedArray::rend() for returning a const reverse iterator
00320   // from the beginning of the fixed array.
00321   const_reverse_iterator rend() const {
00322     return const_reverse_iterator(begin());
00323   }
00324 
00325   // FixedArray::crend()
00326   //
00327   // Returns a reverse iterator from the beginning of the fixed array.
00328   const_reverse_iterator crend() const { return rend(); }
00329 
00330   // FixedArray::fill()
00331   //
00332   // Assigns the given `value` to all elements in the fixed array.
00333   void fill(const value_type& val) { std::fill(begin(), end(), val); }
00334 
00335   // Relational operators. Equality operators are elementwise using
00336   // `operator==`, while order operators order FixedArrays lexicographically.
00337   friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
00338     return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
00339   }
00340 
00341   friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
00342     return !(lhs == rhs);
00343   }
00344 
00345   friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
00346     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
00347                                         rhs.end());
00348   }
00349 
00350   friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
00351     return rhs < lhs;
00352   }
00353 
00354   friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
00355     return !(rhs < lhs);
00356   }
00357 
00358   friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
00359     return !(lhs < rhs);
00360   }
00361 
00362   template <typename H>
00363   friend H AbslHashValue(H h, const FixedArray& v) {
00364     return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
00365                       v.size());
00366   }
00367 
00368  private:
00369   // StorageElement
00370   //
00371   // For FixedArrays with a C-style-array value_type, StorageElement is a POD
00372   // wrapper struct called StorageElementWrapper that holds the value_type
00373   // instance inside. This is needed for construction and destruction of the
00374   // entire array regardless of how many dimensions it has. For all other cases,
00375   // StorageElement is just an alias of value_type.
00376   //
00377   // Maintainer's Note: The simpler solution would be to simply wrap value_type
00378   // in a struct whether it's an array or not. That causes some paranoid
00379   // diagnostics to misfire, believing that 'data()' returns a pointer to a
00380   // single element, rather than the packed array that it really is.
00381   // e.g.:
00382   //
00383   //     FixedArray<char> buf(1);
00384   //     sprintf(buf.data(), "foo");
00385   //
00386   //     error: call to int __builtin___sprintf_chk(etc...)
00387   //     will always overflow destination buffer [-Werror]
00388   //
00389   template <typename OuterT = value_type,
00390             typename InnerT = absl::remove_extent_t<OuterT>,
00391             size_t InnerN = std::extent<OuterT>::value>
00392   struct StorageElementWrapper {
00393     InnerT array[InnerN];
00394   };
00395 
00396   using StorageElement =
00397       absl::conditional_t<std::is_array<value_type>::value,
00398                           StorageElementWrapper<value_type>, value_type>;
00399   using StorageElementBuffer =
00400       absl::aligned_storage_t<sizeof(StorageElement), alignof(StorageElement)>;
00401 
00402   static pointer AsValueType(pointer ptr) { return ptr; }
00403   static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
00404     return std::addressof(ptr->array);
00405   }
00406 
00407   static_assert(sizeof(StorageElement) == sizeof(value_type), "");
00408   static_assert(alignof(StorageElement) == alignof(value_type), "");
00409 
00410   struct NonEmptyInlinedStorage {
00411     StorageElement* data() {
00412       return reinterpret_cast<StorageElement*>(inlined_storage_.data());
00413     }
00414 
00415 #ifdef ADDRESS_SANITIZER
00416     void* RedzoneBegin() { return &redzone_begin_; }
00417     void* RedzoneEnd() { return &redzone_end_ + 1; }
00418 #endif  // ADDRESS_SANITIZER
00419 
00420     void AnnotateConstruct(size_type);
00421     void AnnotateDestruct(size_type);
00422 
00423     ADDRESS_SANITIZER_REDZONE(redzone_begin_);
00424     std::array<StorageElementBuffer, inline_elements> inlined_storage_;
00425     ADDRESS_SANITIZER_REDZONE(redzone_end_);
00426   };
00427 
00428   struct EmptyInlinedStorage {
00429     StorageElement* data() { return nullptr; }
00430     void AnnotateConstruct(size_type) {}
00431     void AnnotateDestruct(size_type) {}
00432   };
00433 
00434   using InlinedStorage =
00435       absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
00436                           NonEmptyInlinedStorage>;
00437 
00438   // Storage
00439   //
00440   // An instance of Storage manages the inline and out-of-line memory for
00441   // instances of FixedArray. This guarantees that even when construction of
00442   // individual elements fails in the FixedArray constructor body, the
00443   // destructor for Storage will still be called and out-of-line memory will be
00444   // properly deallocated.
00445   //
00446   class Storage : public InlinedStorage {
00447    public:
00448     Storage(size_type n, const allocator_type& a)
00449         : size_alloc_(n, a), data_(InitializeData()) {}
00450 
00451     ~Storage() noexcept {
00452       if (UsingInlinedStorage(size())) {
00453         InlinedStorage::AnnotateDestruct(size());
00454       } else {
00455         AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
00456       }
00457     }
00458 
00459     size_type size() const { return size_alloc_.template get<0>(); }
00460     StorageElement* begin() const { return data_; }
00461     StorageElement* end() const { return begin() + size(); }
00462     allocator_type& alloc() {
00463       return size_alloc_.template get<1>();
00464     }
00465 
00466    private:
00467     static bool UsingInlinedStorage(size_type n) {
00468       return n <= inline_elements;
00469     }
00470 
00471     StorageElement* InitializeData() {
00472       if (UsingInlinedStorage(size())) {
00473         InlinedStorage::AnnotateConstruct(size());
00474         return InlinedStorage::data();
00475       } else {
00476         return reinterpret_cast<StorageElement*>(
00477             AllocatorTraits::allocate(alloc(), size()));
00478       }
00479     }
00480 
00481     // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s
00482     container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
00483     StorageElement* data_;
00484   };
00485 
00486   Storage storage_;
00487 };
00488 
00489 template <typename T, size_t N, typename A>
00490 constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
00491 
00492 template <typename T, size_t N, typename A>
00493 constexpr typename FixedArray<T, N, A>::size_type
00494     FixedArray<T, N, A>::inline_elements;
00495 
00496 template <typename T, size_t N, typename A>
00497 void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
00498     typename FixedArray<T, N, A>::size_type n) {
00499 #ifdef ADDRESS_SANITIZER
00500   if (!n) return;
00501   ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n);
00502   ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin());
00503 #endif                   // ADDRESS_SANITIZER
00504   static_cast<void>(n);  // Mark used when not in asan mode
00505 }
00506 
00507 template <typename T, size_t N, typename A>
00508 void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
00509     typename FixedArray<T, N, A>::size_type n) {
00510 #ifdef ADDRESS_SANITIZER
00511   if (!n) return;
00512   ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd());
00513   ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data());
00514 #endif                   // ADDRESS_SANITIZER
00515   static_cast<void>(n);  // Mark used when not in asan mode
00516 }
00517 }  // namespace absl
00518 
00519 #endif  // ABSL_CONTAINER_FIXED_ARRAY_H_


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:14