Coverage Report

Created: 2024-09-23 06:29

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