tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_