Coverage Report

Created: 2026-05-23 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/container/fixed_array.h
Line
Count
Source
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/hardening.h"
48
#include "absl/base/internal/iterator_traits.h"
49
#include "absl/base/macros.h"
50
#include "absl/base/optimization.h"
51
#include "absl/base/port.h"
52
#include "absl/base/throw_delegate.h"
53
#include "absl/container/internal/compressed_tuple.h"
54
#include "absl/hash/internal/weakly_mixed_integer.h"
55
#include "absl/memory/memory.h"
56
57
namespace absl {
58
ABSL_NAMESPACE_BEGIN
59
60
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
61
62
// -----------------------------------------------------------------------------
63
// FixedArray
64
// -----------------------------------------------------------------------------
65
//
66
// A `FixedArray` provides a run-time fixed-size array, allocating a small array
67
// inline for efficiency.
68
//
69
// Most users should not specify the `N` template parameter and let `FixedArray`
70
// automatically determine the number of elements to store inline based on
71
// `sizeof(T)`. If `N` is specified, the `FixedArray` implementation will use
72
// inline storage for arrays with a length <= `N`.
73
//
74
// Note that a `FixedArray` constructed with a `size_type` argument will
75
// default-initialize its values by leaving trivially constructible types
76
// uninitialized (e.g. int, int[4], double), and others default-constructed.
77
// This matches the behavior of c-style arrays and `std::array`, but not
78
// `std::vector`.
79
template <typename T, size_t N = kFixedArrayUseDefault,
80
          typename A = std::allocator<T>>
81
class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray {
82
  static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
83
                "Arrays with unknown bounds cannot be used with FixedArray.");
84
85
  static constexpr size_t kInlineBytesDefault = 256;
86
87
  using AllocatorTraits = std::allocator_traits<A>;
88
  template <typename Iterator>
89
  using EnableIfInputIterator =
90
      std::enable_if_t<base_internal::IsAtLeastInputIterator<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
0
  static constexpr bool DefaultConstructorIsNonTrivial() {
100
0
    return !std::is_trivially_default_constructible<StorageElement>::value;
101
0
  }
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
0
      : storage_(n, a) {
142
0
    if (DefaultConstructorIsNonTrivial()) {
143
0
      memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
144
0
                                      storage_.end());
145
0
    }
146
0
  }
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 input_iterator or better.
164
  template <typename Iterator, EnableIfInputIterator<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
0
  ~FixedArray() noexcept {
172
0
    for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
173
0
      AllocatorTraits::destroy(storage_.alloc(), cur);
174
0
    }
175
0
  }
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
0
  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
0
  pointer data() ABSL_ATTRIBUTE_LIFETIME_BOUND {
218
0
    return AsValueType(storage_.begin());
219
0
  }
220
221
  // FixedArray::operator[]
222
  //
223
  // Returns a reference the ith element of the fixed array.
224
  // REQUIRES: 0 <= i < size()
225
0
  reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
226
0
    absl::base_internal::HardeningAssertLT(i, size());
227
0
    return data()[i];
228
0
  }
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::base_internal::HardeningAssertLT(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
      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
      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::base_internal::HardeningAssertNonEmpty(*this);
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::base_internal::HardeningAssertNonEmpty(*this);
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::base_internal::HardeningAssertNonEmpty(*this);
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::base_internal::HardeningAssertNonEmpty(*this);
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_contiguous(std::move(h), v.data(), 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 = std::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
      std::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) unsigned char buff_[sizeof(
450
        StorageElement[inline_elements])];
451
    ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_);
452
  };
453
454
  class EmptyInlinedStorage {
455
   public:
456
    StorageElement* data() { return nullptr; }
457
    void AnnotateConstruct(size_type) {}
458
    void AnnotateDestruct(size_type) {}
459
  };
460
461
  using InlinedStorage =
462
      std::conditional_t<inline_elements == 0, EmptyInlinedStorage,
463
                          NonEmptyInlinedStorage>;
464
465
  // Storage
466
  //
467
  // An instance of Storage manages the inline and out-of-line memory for
468
  // instances of FixedArray. This guarantees that even when construction of
469
  // individual elements fails in the FixedArray constructor body, the
470
  // destructor for Storage will still be called and out-of-line memory will be
471
  // properly deallocated.
472
  //
473
  class Storage : public InlinedStorage {
474
   public:
475
    Storage(size_type n, const allocator_type& a)
476
0
        : size_alloc_(n, a), data_(InitializeData()) {}
477
478
0
    ~Storage() noexcept {
479
0
      if (UsingInlinedStorage(size())) {
480
0
        InlinedStorage::AnnotateDestruct(size());
481
0
      } else {
482
0
        AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
483
0
      }
484
0
    }
485
486
0
    size_type size() const { return size_alloc_.template get<0>(); }
487
0
    StorageElement* begin() const { return data_; }
488
0
    StorageElement* end() const { return begin() + size(); }
489
0
    allocator_type& alloc() { return size_alloc_.template get<1>(); }
490
    const allocator_type& alloc() const {
491
      return size_alloc_.template get<1>();
492
    }
493
494
   private:
495
0
    static bool UsingInlinedStorage(size_type n) {
496
0
      return n <= inline_elements;
497
0
    }
498
499
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
500
    ABSL_ATTRIBUTE_NOINLINE
501
#endif  // ABSL_HAVE_ADDRESS_SANITIZER
502
0
    StorageElement* InitializeData() {
503
0
      if (UsingInlinedStorage(size())) {
504
0
        InlinedStorage::AnnotateConstruct(size());
505
0
        return InlinedStorage::data();
506
0
      } else {
507
0
        return reinterpret_cast<StorageElement*>(
508
0
            AllocatorTraits::allocate(alloc(), size()));
509
0
      }
510
0
    }
511
512
    // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s
513
    container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
514
    StorageElement* data_;
515
  };
516
517
  Storage storage_;
518
};
519
520
template <typename T, size_t N, typename A>
521
void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
522
0
    typename FixedArray<T, N, A>::size_type n) {
523
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
524
  if (!n) return;
525
  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(),
526
                                     data() + n);
527
  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(),
528
                                     RedzoneBegin());
529
#endif  // ABSL_HAVE_ADDRESS_SANITIZER
530
0
  static_cast<void>(n);  // Mark used when not in asan mode
531
0
}
532
533
template <typename T, size_t N, typename A>
534
void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
535
0
    typename FixedArray<T, N, A>::size_type n) {
536
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
537
  if (!n) return;
538
  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n,
539
                                     RedzoneEnd());
540
  ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(),
541
                                     data());
542
#endif  // ABSL_HAVE_ADDRESS_SANITIZER
543
0
  static_cast<void>(n);  // Mark used when not in asan mode
544
0
}
545
ABSL_NAMESPACE_END
546
}  // namespace absl
547
548
#endif  // ABSL_CONTAINER_FIXED_ARRAY_H_