blob: dcbddcd3a1d14698dc2d07799b842aa9c9f111d6 [file] [log] [blame]
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001// Copyright 2018 The Abseil Authors.
Austin Schuh70cc9552019-01-21 19:46:48 -08002//
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08003// 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
Austin Schuh70cc9552019-01-21 19:46:48 -08006//
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08007// https://www.apache.org/licenses/LICENSE-2.0
Austin Schuh70cc9552019-01-21 19:46:48 -08008//
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08009// 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.
Austin Schuh70cc9552019-01-21 19:46:48 -080014//
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080015// -----------------------------------------------------------------------------
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.
Austin Schuh70cc9552019-01-21 19:46:48 -080029
30#ifndef CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
31#define CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
32
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080033#include <Eigen/Core> // For Eigen::aligned_allocator
34#include <algorithm>
35#include <array>
Austin Schuh70cc9552019-01-21 19:46:48 -080036#include <cstddef>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080037#include <memory>
38#include <tuple>
39#include <type_traits>
40
41#include "ceres/internal/memory.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080042#include "glog/logging.h"
43
44namespace ceres {
45namespace internal {
46
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080047constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
Austin Schuh70cc9552019-01-21 19:46:48 -080048
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080049// The default fixed array allocator.
Austin Schuh70cc9552019-01-21 19:46:48 -080050//
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080051// As one can not easily detect if a struct contains or inherits from a fixed
52// size Eigen type, to be safe the Eigen::aligned_allocator is used by default.
53// But trivial types can never contain Eigen types, so std::allocator is used to
54// safe some heap memory.
55template <typename T>
56using FixedArrayDefaultAllocator =
57 typename std::conditional<std::is_trivial<T>::value,
58 std::allocator<T>,
59 Eigen::aligned_allocator<T>>::type;
Austin Schuh70cc9552019-01-21 19:46:48 -080060
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080061// -----------------------------------------------------------------------------
62// FixedArray
63// -----------------------------------------------------------------------------
64//
65// A `FixedArray` provides a run-time fixed-size array, allocating a small array
66// inline for efficiency.
67//
68// Most users should not specify an `inline_elements` argument and let
69// `FixedArray` automatically determine the number of elements
70// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
71// `FixedArray` implementation will use inline storage for arrays with a
72// length <= `inline_elements`.
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//
80// Note that `FixedArray` does not provide a public allocator; if it requires a
81// heap allocation, it will do so with global `::operator new[]()` and
82// `::operator delete[]()`, even if T provides class-scope overrides for these
83// operators.
84template <typename T,
85 size_t N = kFixedArrayUseDefault,
86 typename A = FixedArrayDefaultAllocator<T>>
Austin Schuh70cc9552019-01-21 19:46:48 -080087class FixedArray {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080088 static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
89 "Arrays with unknown bounds cannot be used with FixedArray.");
90
91 static constexpr size_t kInlineBytesDefault = 256;
92
93 using AllocatorTraits = std::allocator_traits<A>;
94 // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
95 // but this seems to be mostly pedantic.
96 template <typename Iterator>
97 using EnableIfForwardIterator = typename std::enable_if<std::is_convertible<
98 typename std::iterator_traits<Iterator>::iterator_category,
99 std::forward_iterator_tag>::value>::type;
100 static constexpr bool DefaultConstructorIsNonTrivial() {
101 return !std::is_trivially_default_constructible<StorageElement>::value;
102 }
103
Austin Schuh70cc9552019-01-21 19:46:48 -0800104 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800105 using allocator_type = typename AllocatorTraits::allocator_type;
106 using value_type = typename AllocatorTraits::value_type;
107 using pointer = typename AllocatorTraits::pointer;
108 using const_pointer = typename AllocatorTraits::const_pointer;
109 using reference = value_type&;
110 using const_reference = const value_type&;
111 using size_type = typename AllocatorTraits::size_type;
112 using difference_type = typename AllocatorTraits::difference_type;
113 using iterator = pointer;
114 using const_iterator = const_pointer;
115 using reverse_iterator = std::reverse_iterator<iterator>;
116 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
Austin Schuh70cc9552019-01-21 19:46:48 -0800117
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800118 static constexpr size_type inline_elements =
119 (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
120 : static_cast<size_type>(N));
121
122 FixedArray(const FixedArray& other,
123 const allocator_type& a = allocator_type())
124 : FixedArray(other.begin(), other.end(), a) {}
125
126 FixedArray(FixedArray&& other, const allocator_type& a = allocator_type())
127 : FixedArray(std::make_move_iterator(other.begin()),
128 std::make_move_iterator(other.end()),
129 a) {}
130
131 // Creates an array object that can store `n` elements.
132 // Note that trivially constructible elements will be uninitialized.
133 explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
134 : storage_(n, a) {
135 if (DefaultConstructorIsNonTrivial()) {
136 ConstructRange(storage_.alloc(), storage_.begin(), storage_.end());
137 }
138 }
139
140 // Creates an array initialized with `n` copies of `val`.
141 FixedArray(size_type n,
142 const value_type& val,
143 const allocator_type& a = allocator_type())
144 : storage_(n, a) {
145 ConstructRange(storage_.alloc(), storage_.begin(), storage_.end(), val);
146 }
147
148 // Creates an array initialized with the size and contents of `init_list`.
149 FixedArray(std::initializer_list<value_type> init_list,
150 const allocator_type& a = allocator_type())
151 : FixedArray(init_list.begin(), init_list.end(), a) {}
152
153 // Creates an array initialized with the elements from the input
154 // range. The array's size will always be `std::distance(first, last)`.
155 // REQUIRES: Iterator must be a forward_iterator or better.
156 template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
157 FixedArray(Iterator first,
158 Iterator last,
159 const allocator_type& a = allocator_type())
160 : storage_(std::distance(first, last), a) {
161 CopyRange(storage_.alloc(), storage_.begin(), first, last);
162 }
163
164 ~FixedArray() noexcept {
165 for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
166 AllocatorTraits::destroy(storage_.alloc(), cur);
167 }
168 }
169
170 // Assignments are deleted because they break the invariant that the size of a
171 // `FixedArray` never changes.
172 void operator=(FixedArray&&) = delete;
173 void operator=(const FixedArray&) = delete;
174
175 // FixedArray::size()
Austin Schuh70cc9552019-01-21 19:46:48 -0800176 //
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800177 // Returns the length of the fixed array.
178 size_type size() const { return storage_.size(); }
Austin Schuh70cc9552019-01-21 19:46:48 -0800179
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800180 // FixedArray::max_size()
181 //
182 // Returns the largest possible value of `std::distance(begin(), end())` for a
183 // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
184 // over the number of bytes taken by T.
185 constexpr size_type max_size() const {
186 return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
Austin Schuh70cc9552019-01-21 19:46:48 -0800187 }
188
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800189 // FixedArray::empty()
190 //
191 // Returns whether or not the fixed array is empty.
192 bool empty() const { return size() == 0; }
193
194 // FixedArray::memsize()
195 //
196 // Returns the memory size of the fixed array in bytes.
197 size_t memsize() const { return size() * sizeof(value_type); }
198
199 // FixedArray::data()
200 //
201 // Returns a const T* pointer to elements of the `FixedArray`. This pointer
202 // can be used to access (but not modify) the contained elements.
203 const_pointer data() const { return AsValueType(storage_.begin()); }
204
205 // Overload of FixedArray::data() to return a T* pointer to elements of the
206 // fixed array. This pointer can be used to access and modify the contained
207 // elements.
208 pointer data() { return AsValueType(storage_.begin()); }
209
210 // FixedArray::operator[]
211 //
212 // Returns a reference the ith element of the fixed array.
Austin Schuh70cc9552019-01-21 19:46:48 -0800213 // REQUIRES: 0 <= i < size()
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800214 reference operator[](size_type i) {
215 DCHECK_LT(i, size());
216 return data()[i];
Austin Schuh70cc9552019-01-21 19:46:48 -0800217 }
218
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800219 // Overload of FixedArray::operator()[] to return a const reference to the
220 // ith element of the fixed array.
221 // REQUIRES: 0 <= i < size()
222 const_reference operator[](size_type i) const {
223 DCHECK_LT(i, size());
224 return data()[i];
225 }
Austin Schuh70cc9552019-01-21 19:46:48 -0800226
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800227 // FixedArray::front()
228 //
229 // Returns a reference to the first element of the fixed array.
230 reference front() { return *begin(); }
231
232 // Overload of FixedArray::front() to return a reference to the first element
233 // of a fixed array of const values.
234 const_reference front() const { return *begin(); }
235
236 // FixedArray::back()
237 //
238 // Returns a reference to the last element of the fixed array.
239 reference back() { return *(end() - 1); }
240
241 // Overload of FixedArray::back() to return a reference to the last element
242 // of a fixed array of const values.
243 const_reference back() const { return *(end() - 1); }
244
245 // FixedArray::begin()
246 //
247 // Returns an iterator to the beginning of the fixed array.
248 iterator begin() { return data(); }
249
250 // Overload of FixedArray::begin() to return a const iterator to the
251 // beginning of the fixed array.
252 const_iterator begin() const { return data(); }
253
254 // FixedArray::cbegin()
255 //
256 // Returns a const iterator to the beginning of the fixed array.
257 const_iterator cbegin() const { return begin(); }
258
259 // FixedArray::end()
260 //
261 // Returns an iterator to the end of the fixed array.
262 iterator end() { return data() + size(); }
263
264 // Overload of FixedArray::end() to return a const iterator to the end of the
265 // fixed array.
266 const_iterator end() const { return data() + size(); }
267
268 // FixedArray::cend()
269 //
270 // Returns a const iterator to the end of the fixed array.
271 const_iterator cend() const { return end(); }
272
273 // FixedArray::rbegin()
274 //
275 // Returns a reverse iterator from the end of the fixed array.
276 reverse_iterator rbegin() { return reverse_iterator(end()); }
277
278 // Overload of FixedArray::rbegin() to return a const reverse iterator from
279 // the end of the fixed array.
280 const_reverse_iterator rbegin() const {
281 return const_reverse_iterator(end());
282 }
283
284 // FixedArray::crbegin()
285 //
286 // Returns a const reverse iterator from the end of the fixed array.
287 const_reverse_iterator crbegin() const { return rbegin(); }
288
289 // FixedArray::rend()
290 //
291 // Returns a reverse iterator from the beginning of the fixed array.
292 reverse_iterator rend() { return reverse_iterator(begin()); }
293
294 // Overload of FixedArray::rend() for returning a const reverse iterator
295 // from the beginning of the fixed array.
296 const_reverse_iterator rend() const {
297 return const_reverse_iterator(begin());
298 }
299
300 // FixedArray::crend()
301 //
302 // Returns a reverse iterator from the beginning of the fixed array.
303 const_reverse_iterator crend() const { return rend(); }
304
305 // FixedArray::fill()
306 //
307 // Assigns the given `value` to all elements in the fixed array.
308 void fill(const value_type& val) { std::fill(begin(), end(), val); }
309
310 // Relational operators. Equality operators are elementwise using
311 // `operator==`, while order operators order FixedArrays lexicographically.
312 friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
313 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
314 }
315
316 friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
317 return !(lhs == rhs);
318 }
319
320 friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
321 return std::lexicographical_compare(
322 lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
323 }
324
325 friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
326 return rhs < lhs;
327 }
328
329 friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
330 return !(rhs < lhs);
331 }
332
333 friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
334 return !(lhs < rhs);
335 }
Austin Schuh70cc9552019-01-21 19:46:48 -0800336
337 private:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800338 // StorageElement
339 //
340 // For FixedArrays with a C-style-array value_type, StorageElement is a POD
341 // wrapper struct called StorageElementWrapper that holds the value_type
342 // instance inside. This is needed for construction and destruction of the
343 // entire array regardless of how many dimensions it has. For all other cases,
344 // StorageElement is just an alias of value_type.
345 //
346 // Maintainer's Note: The simpler solution would be to simply wrap value_type
347 // in a struct whether it's an array or not. That causes some paranoid
348 // diagnostics to misfire, believing that 'data()' returns a pointer to a
349 // single element, rather than the packed array that it really is.
350 // e.g.:
351 //
352 // FixedArray<char> buf(1);
353 // sprintf(buf.data(), "foo");
354 //
355 // error: call to int __builtin___sprintf_chk(etc...)
356 // will always overflow destination buffer [-Werror]
357 //
358 template <typename OuterT,
359 typename InnerT = typename std::remove_extent<OuterT>::type,
360 size_t InnerN = std::extent<OuterT>::value>
361 struct StorageElementWrapper {
362 InnerT array[InnerN];
Austin Schuh70cc9552019-01-21 19:46:48 -0800363 };
364
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800365 using StorageElement =
366 typename std::conditional<std::is_array<value_type>::value,
367 StorageElementWrapper<value_type>,
368 value_type>::type;
Austin Schuh70cc9552019-01-21 19:46:48 -0800369
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800370 static pointer AsValueType(pointer ptr) { return ptr; }
371 static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
372 return std::addressof(ptr->array);
373 }
Austin Schuh70cc9552019-01-21 19:46:48 -0800374
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800375 static_assert(sizeof(StorageElement) == sizeof(value_type), "");
376 static_assert(alignof(StorageElement) == alignof(value_type), "");
377
378 class NonEmptyInlinedStorage {
379 public:
380 StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
381 void AnnotateConstruct(size_type) {}
382 void AnnotateDestruct(size_type) {}
383
384 // #ifdef ADDRESS_SANITIZER
385 // void* RedzoneBegin() { return &redzone_begin_; }
386 // void* RedzoneEnd() { return &redzone_end_ + 1; }
387 // #endif // ADDRESS_SANITIZER
388
389 private:
390 // ADDRESS_SANITIZER_REDZONE(redzone_begin_);
391 alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])];
392 // ADDRESS_SANITIZER_REDZONE(redzone_end_);
393 };
394
395 class EmptyInlinedStorage {
396 public:
397 StorageElement* data() { return nullptr; }
398 void AnnotateConstruct(size_type) {}
399 void AnnotateDestruct(size_type) {}
400 };
401
402 using InlinedStorage =
403 typename std::conditional<inline_elements == 0,
404 EmptyInlinedStorage,
405 NonEmptyInlinedStorage>::type;
406
407 // Storage
408 //
409 // An instance of Storage manages the inline and out-of-line memory for
410 // instances of FixedArray. This guarantees that even when construction of
411 // individual elements fails in the FixedArray constructor body, the
412 // destructor for Storage will still be called and out-of-line memory will be
413 // properly deallocated.
414 //
415 class Storage : public InlinedStorage {
416 public:
417 Storage(size_type n, const allocator_type& a)
418 : size_alloc_(n, a), data_(InitializeData()) {}
419
420 ~Storage() noexcept {
421 if (UsingInlinedStorage(size())) {
422 InlinedStorage::AnnotateDestruct(size());
423 } else {
424 AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
425 }
426 }
427
428 size_type size() const { return std::get<0>(size_alloc_); }
429 StorageElement* begin() const { return data_; }
430 StorageElement* end() const { return begin() + size(); }
431 allocator_type& alloc() { return std::get<1>(size_alloc_); }
432
433 private:
434 static bool UsingInlinedStorage(size_type n) {
435 return n <= inline_elements;
436 }
437
438 StorageElement* InitializeData() {
439 if (UsingInlinedStorage(size())) {
440 InlinedStorage::AnnotateConstruct(size());
441 return InlinedStorage::data();
442 } else {
443 return reinterpret_cast<StorageElement*>(
444 AllocatorTraits::allocate(alloc(), size()));
445 }
446 }
447
448 // Using std::tuple and not absl::CompressedTuple, as it has a lot of
449 // dependencies to other absl headers.
450 std::tuple<size_type, allocator_type> size_alloc_;
451 StorageElement* data_;
452 };
453
454 Storage storage_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800455};
456
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800457template <typename T, size_t N, typename A>
458constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
Austin Schuh70cc9552019-01-21 19:46:48 -0800459
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800460template <typename T, size_t N, typename A>
461constexpr typename FixedArray<T, N, A>::size_type
462 FixedArray<T, N, A>::inline_elements;
Austin Schuh70cc9552019-01-21 19:46:48 -0800463
464} // namespace internal
465} // namespace ceres
466
467#endif // CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_