blob: 20bde27285b815b3bb34237b9ccbb233e843537c [file] [log] [blame]
mistergc2e75482017-09-19 16:54:40 -04001// Copyright 2017 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// http://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 <array>
35#include <cassert>
36#include <cstddef>
37#include <initializer_list>
38#include <iterator>
39#include <limits>
40#include <memory>
41#include <new>
42#include <type_traits>
43
44#include "absl/algorithm/algorithm.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
51namespace absl {
52
53constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
54
55// -----------------------------------------------------------------------------
56// FixedArray
57// -----------------------------------------------------------------------------
58//
59// A `FixedArray` provides a run-time fixed-size array, allocating small arrays
60// inline for efficiency and correctness.
61//
62// Most users should not specify an `inline_elements` argument and let
63// `FixedArray<>` automatically determine the number of elements
64// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
65// `FixedArray<>` implementation will inline arrays of
66// length <= `inline_elements`.
67//
68// Note that a `FixedArray` constructed with a `size_type` argument will
69// default-initialize its values by leaving trivially constructible types
70// uninitialized (e.g. int, int[4], double), and others default-constructed.
71// This matches the behavior of c-style arrays and `std::array`, but not
72// `std::vector`.
73//
74// Note that `FixedArray` does not provide a public allocator; if it requires a
75// heap allocation, it will do so with global `::operator new[]()` and
76// `::operator delete[]()`, even if T provides class-scope overrides for these
77// operators.
78template <typename T, size_t inlined = kFixedArrayUseDefault>
79class FixedArray {
80 static constexpr size_t kInlineBytesDefault = 256;
81
82 // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
83 // but this seems to be mostly pedantic.
84 template <typename Iter>
85 using EnableIfForwardIterator = typename std::enable_if<
86 std::is_convertible<
87 typename std::iterator_traits<Iter>::iterator_category,
88 std::forward_iterator_tag>::value,
89 int>::type;
90
91 public:
92 // For playing nicely with stl:
93 using value_type = T;
94 using iterator = T*;
95 using const_iterator = const T*;
96 using reverse_iterator = std::reverse_iterator<iterator>;
97 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
98 using reference = T&;
99 using const_reference = const T&;
100 using pointer = T*;
101 using const_pointer = const T*;
102 using difference_type = ptrdiff_t;
103 using size_type = size_t;
104
105 static constexpr size_type inline_elements =
106 inlined == kFixedArrayUseDefault
107 ? kInlineBytesDefault / sizeof(value_type)
108 : inlined;
109
110 // Creates an array object that can store `n` elements.
111 // Note that trivially constructible elements will be uninitialized.
112 explicit FixedArray(size_type n) : rep_(n) {}
113
114 // Creates an array initialized with `n` copies of `val`.
115 FixedArray(size_type n, const value_type& val) : rep_(n, val) {}
116
117 // Creates an array initialized with the elements from the input
118 // range. The array's size will always be `std::distance(first, last)`.
119 // REQUIRES: Iter must be a forward_iterator or better.
120 template <typename Iter, EnableIfForwardIterator<Iter> = 0>
121 FixedArray(Iter first, Iter last) : rep_(first, last) {}
122
123 // Create the array from an initializer_list.
124 FixedArray(std::initializer_list<T> init_list)
125 : FixedArray(init_list.begin(), init_list.end()) {}
126
127 ~FixedArray() {}
128
129 // Copy and move construction and assignment are deleted because (1) you can't
130 // copy or move an array, (2) assignment breaks the invariant that the size of
131 // a `FixedArray` never changes, and (3) there's no clear answer as to what
132 // should happen to a moved-from `FixedArray`.
133 FixedArray(const FixedArray&) = delete;
134 void operator=(const FixedArray&) = delete;
135
136 // FixedArray::size()
137 //
138 // Returns the length of the fixed array.
139 size_type size() const { return rep_.size(); }
140
141 // FixedArray::max_size()
142 //
143 // Returns the largest possible value of `std::distance(begin(), end())` for a
144 // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
145 // over the number of bytes taken by T.
146 constexpr size_type max_size() const {
147 return std::numeric_limits<difference_type>::max() / sizeof(value_type);
148 }
149
150 // FixedArray::empty()
151 //
152 // Returns whether or not the fixed array is empty.
153 bool empty() const { return size() == 0; }
154
155 // FixedArray::memsize()
156 //
157 // Returns the memory size of the fixed array in bytes.
158 size_t memsize() const { return size() * sizeof(value_type); }
159
160 // FixedArray::data()
161 //
162 // Returns a const T* pointer to elements of the `FixedArray`. This pointer
163 // can be used to access (but not modify) the contained elements.
164 const_pointer data() const { return AsValue(rep_.begin()); }
165
166 // Overload of FixedArray::data() to return a T* pointer to elements of the
167 // fixed array. This pointer can be used to access and modify the contained
168 // elements.
169 pointer data() { return AsValue(rep_.begin()); }
170 // FixedArray::operator[]
171 //
172 // Returns a reference the ith element of the fixed array.
173 // REQUIRES: 0 <= i < size()
174 reference operator[](size_type i) {
175 assert(i < size());
176 return data()[i];
177 }
178
179 // Overload of FixedArray::operator()[] to return a const reference to the
180 // ith element of the fixed array.
181 // REQUIRES: 0 <= i < size()
182 const_reference operator[](size_type i) const {
183 assert(i < size());
184 return data()[i];
185 }
186
187 // FixedArray::at
188 //
189 // Bounds-checked access. Returns a reference to the ith element of the
190 // fiexed array, or throws std::out_of_range
191 reference at(size_type i) {
192 if (ABSL_PREDICT_FALSE(i >= size())) {
193 base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
194 }
195 return data()[i];
196 }
197
198 // Overload of FixedArray::at() to return a const reference to the ith element
199 // of the fixed array.
200 const_reference at(size_type i) const {
201 if (i >= size()) {
202 base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
203 }
204 return data()[i];
205 }
206
207 // FixedArray::front()
208 //
209 // Returns a reference to the first element of the fixed array.
210 reference front() { return *begin(); }
211
212 // Overload of FixedArray::front() to return a reference to the first element
213 // of a fixed array of const values.
214 const_reference front() const { return *begin(); }
215
216 // FixedArray::back()
217 //
218 // Returns a reference to the last element of the fixed array.
219 reference back() { return *(end() - 1); }
220
221 // Overload of FixedArray::back() to return a reference to the last element
222 // of a fixed array of const values.
223 const_reference back() const { return *(end() - 1); }
224
225 // FixedArray::begin()
226 //
227 // Returns an iterator to the beginning of the fixed array.
228 iterator begin() { return data(); }
229
230 // Overload of FixedArray::begin() to return a const iterator to the
231 // beginning of the fixed array.
232 const_iterator begin() const { return data(); }
233
234 // FixedArray::cbegin()
235 //
236 // Returns a const iterator to the beginning of the fixed array.
237 const_iterator cbegin() const { return begin(); }
238
239 // FixedArray::end()
240 //
241 // Returns an iterator to the end of the fixed array.
242 iterator end() { return data() + size(); }
243
244 // Overload of FixedArray::end() to return a const iterator to the end of the
245 // fixed array.
246 const_iterator end() const { return data() + size(); }
247
248 // FixedArray::cend()
249 //
250 // Returns a const iterator to the end of the fixed array.
251 const_iterator cend() const { return end(); }
252
253 // FixedArray::rbegin()
254 //
255 // Returns a reverse iterator from the end of the fixed array.
256 reverse_iterator rbegin() { return reverse_iterator(end()); }
257
258 // Overload of FixedArray::rbegin() to return a const reverse iterator from
259 // the end of the fixed array.
260 const_reverse_iterator rbegin() const {
261 return const_reverse_iterator(end());
262 }
263
264 // FixedArray::crbegin()
265 //
266 // Returns a const reverse iterator from the end of the fixed array.
267 const_reverse_iterator crbegin() const { return rbegin(); }
268
269 // FixedArray::rend()
270 //
271 // Returns a reverse iterator from the beginning of the fixed array.
272 reverse_iterator rend() { return reverse_iterator(begin()); }
273
274 // Overload of FixedArray::rend() for returning a const reverse iterator
275 // from the beginning of the fixed array.
276 const_reverse_iterator rend() const {
277 return const_reverse_iterator(begin());
278 }
279
280 // FixedArray::crend()
281 //
282 // Returns a reverse iterator from the beginning of the fixed array.
283 const_reverse_iterator crend() const { return rend(); }
284
285 // FixedArray::fill()
286 //
287 // Assigns the given `value` to all elements in the fixed array.
288 void fill(const T& value) { std::fill(begin(), end(), value); }
289
290 // Relational operators. Equality operators are elementwise using
291 // `operator==`, while order operators order FixedArrays lexicographically.
292 friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
293 return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
294 }
295
296 friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
297 return !(lhs == rhs);
298 }
299
300 friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
301 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
302 rhs.end());
303 }
304
305 friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
306 return rhs < lhs;
307 }
308
309 friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
310 return !(rhs < lhs);
311 }
312
313 friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
314 return !(lhs < rhs);
315 }
316
317 private:
318 // HolderTraits
319 //
320 // Wrapper to hold elements of type T for the case where T is an array type.
321 // If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'.
322 // Otherwise, HolderTraits::type is simply 'T'.
323 //
324 // Maintainer's Note: The simpler solution would be to simply wrap T in a
325 // struct whether it's an array or not: 'struct Holder { T v; };', but
326 // that causes some paranoid diagnostics to misfire about uses of data(),
327 // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
328 // element, rather than the packed array that it really is.
329 // e.g.:
330 //
331 // FixedArray<char> buf(1);
332 // sprintf(buf.data(), "foo");
333 //
334 // error: call to int __builtin___sprintf_chk(etc...)
335 // will always overflow destination buffer [-Werror]
336 //
337 class HolderTraits {
338 template <typename U>
339 struct SelectImpl {
340 using type = U;
341 static pointer AsValue(type* p) { return p; }
342 };
343
344 // Partial specialization for elements of array type.
345 template <typename U, size_t N>
346 struct SelectImpl<U[N]> {
347 struct Holder { U v[N]; };
348 using type = Holder;
349 static pointer AsValue(type* p) { return &p->v; }
350 };
351 using Impl = SelectImpl<value_type>;
352
353 public:
354 using type = typename Impl::type;
355
356 static pointer AsValue(type *p) { return Impl::AsValue(p); }
357
358 // TODO(billydonahue): fix the type aliasing violation
359 // this assertion hints at.
360 static_assert(sizeof(type) == sizeof(value_type),
361 "Holder must be same size as value_type");
362 };
363
364 using Holder = typename HolderTraits::type;
365 static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); }
366
367 // InlineSpace
368 //
369 // Allocate some space, not an array of elements of type T, so that we can
370 // skip calling the T constructors and destructors for space we never use.
371 // How many elements should we store inline?
372 // a. If not specified, use a default of kInlineBytesDefault bytes (This is
373 // currently 256 bytes, which seems small enough to not cause stack overflow
374 // or unnecessary stack pollution, while still allowing stack allocation for
375 // reasonably long character arrays).
376 // b. Never use 0 length arrays (not ISO C++)
377 //
378 template <size_type N, typename = void>
379 class InlineSpace {
380 public:
381 Holder* data() { return reinterpret_cast<Holder*>(space_.data()); }
382 void AnnotateConstruct(size_t n) const { Annotate(n, true); }
383 void AnnotateDestruct(size_t n) const { Annotate(n, false); }
384
385 private:
386#ifndef ADDRESS_SANITIZER
387 void Annotate(size_t, bool) const { }
388#else
389 void Annotate(size_t n, bool creating) const {
390 if (!n) return;
391 const void* bot = &left_redzone_;
392 const void* beg = space_.data();
393 const void* end = space_.data() + n;
394 const void* top = &right_redzone_ + 1;
395 // args: (beg, end, old_mid, new_mid)
396 if (creating) {
397 ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end);
398 ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot);
399 } else {
400 ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top);
401 ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg);
402 }
403 }
404#endif // ADDRESS_SANITIZER
405
406 using Buffer =
407 typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type;
408
409 ADDRESS_SANITIZER_REDZONE(left_redzone_);
410 std::array<Buffer, N> space_;
411 ADDRESS_SANITIZER_REDZONE(right_redzone_);
412 };
413
414 // specialization when N = 0.
415 template <typename U>
416 class InlineSpace<0, U> {
417 public:
418 Holder* data() { return nullptr; }
419 void AnnotateConstruct(size_t) const {}
420 void AnnotateDestruct(size_t) const {}
421 };
422
423 // Rep
424 //
425 // A const Rep object holds FixedArray's size and data pointer.
426 //
427 class Rep : public InlineSpace<inline_elements> {
428 public:
429 Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) {
430 std::uninitialized_fill_n(p_, n, val);
431 }
432
433 explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {
434 // Loop optimizes to nothing for trivially constructible T.
435 for (Holder* p = p_; p != p_ + n; ++p)
436 // Note: no parens: default init only.
437 // Also note '::' to avoid Holder class placement new operator.
438 ::new (static_cast<void*>(p)) Holder;
439 }
440
441 template <typename Iter>
442 Rep(Iter first, Iter last)
443 : n_(std::distance(first, last)), p_(MakeHolder(n_)) {
444 std::uninitialized_copy(first, last, AsValue(p_));
445 }
446
447 ~Rep() {
448 // Destruction must be in reverse order.
449 // Loop optimizes to nothing for trivially destructible T.
450 for (Holder* p = end(); p != begin();) (--p)->~Holder();
451 if (IsAllocated(size())) {
452 ::operator delete[](begin());
453 } else {
454 this->AnnotateDestruct(size());
455 }
456 }
457 Holder* begin() const { return p_; }
458 Holder* end() const { return p_ + n_; }
459 size_type size() const { return n_; }
460
461 private:
462 Holder* MakeHolder(size_type n) {
463 if (IsAllocated(n)) {
464 return Allocate(n);
465 } else {
466 this->AnnotateConstruct(n);
467 return this->data();
468 }
469 }
470
471 Holder* Allocate(size_type n) {
472 return static_cast<Holder*>(::operator new[](n * sizeof(Holder)));
473 }
474
475 bool IsAllocated(size_type n) const { return n > inline_elements; }
476
477 const size_type n_;
478 Holder* const p_;
479 };
480
481
482 // Data members
483 Rep rep_;
484};
485
486template <typename T, size_t N>
487constexpr size_t FixedArray<T, N>::inline_elements;
488
489template <typename T, size_t N>
490constexpr size_t FixedArray<T, N>::kInlineBytesDefault;
491
492} // namespace absl
493#endif // ABSL_CONTAINER_FIXED_ARRAY_H_