kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 11 | #ifndef API_ARRAY_VIEW_H_ |
| 12 | #define API_ARRAY_VIEW_H_ |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 13 | |
| 14 | #include <algorithm> |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 15 | #include <array> |
Alessio Bazzica | e2c5485 | 2020-10-20 15:24:55 | [diff] [blame] | 16 | #include <iterator> |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 17 | #include <type_traits> |
| 18 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 19 | #include "rtc_base/checks.h" |
| 20 | #include "rtc_base/type_traits.h" |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 21 | |
| 22 | namespace rtc { |
| 23 | |
| 24 | // tl;dr: rtc::ArrayView is the same thing as gsl::span from the Guideline |
| 25 | // Support Library. |
| 26 | // |
| 27 | // Many functions read from or write to arrays. The obvious way to do this is |
| 28 | // to use two arguments, a pointer to the first element and an element count: |
| 29 | // |
| 30 | // bool Contains17(const int* arr, size_t size) { |
| 31 | // for (size_t i = 0; i < size; ++i) { |
| 32 | // if (arr[i] == 17) |
| 33 | // return true; |
| 34 | // } |
| 35 | // return false; |
| 36 | // } |
| 37 | // |
| 38 | // This is flexible, since it doesn't matter how the array is stored (C array, |
| 39 | // std::vector, rtc::Buffer, ...), but it's error-prone because the caller has |
| 40 | // to correctly specify the array length: |
| 41 | // |
| 42 | // Contains17(arr, arraysize(arr)); // C array |
| 43 | // Contains17(arr.data(), arr.size()); // std::vector |
| 44 | // Contains17(arr, size); // pointer + size |
| 45 | // ... |
| 46 | // |
| 47 | // It's also kind of messy to have two separate arguments for what is |
| 48 | // conceptually a single thing. |
| 49 | // |
| 50 | // Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't |
| 51 | // own) and a count, and supports the basic things you'd expect, such as |
| 52 | // indexing and iteration. It allows us to write our function like this: |
| 53 | // |
| 54 | // bool Contains17(rtc::ArrayView<const int> arr) { |
| 55 | // for (auto e : arr) { |
| 56 | // if (e == 17) |
| 57 | // return true; |
| 58 | // } |
| 59 | // return false; |
| 60 | // } |
| 61 | // |
| 62 | // And even better, because a bunch of things will implicitly convert to |
| 63 | // ArrayView, we can call it like this: |
| 64 | // |
| 65 | // Contains17(arr); // C array |
| 66 | // Contains17(arr); // std::vector |
| 67 | // Contains17(rtc::ArrayView<int>(arr, size)); // pointer + size |
| 68 | // Contains17(nullptr); // nullptr -> empty ArrayView |
| 69 | // ... |
| 70 | // |
| 71 | // ArrayView<T> stores both a pointer and a size, but you may also use |
| 72 | // ArrayView<T, N>, which has a size that's fixed at compile time (which means |
| 73 | // it only has to store the pointer). |
| 74 | // |
| 75 | // One important point is that ArrayView<T> and ArrayView<const T> are |
| 76 | // different types, which allow and don't allow mutation of the array elements, |
| 77 | // respectively. The implicit conversions work just like you'd hope, so that |
| 78 | // e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const |
| 79 | // int>, but const vector<int> will convert only to ArrayView<const int>. |
| 80 | // (ArrayView itself can be the source type in such conversions, so |
| 81 | // ArrayView<int> will convert to ArrayView<const int>.) |
| 82 | // |
| 83 | // Note: ArrayView is tiny (just a pointer and a count if variable-sized, just |
| 84 | // a pointer if fix-sized) and trivially copyable, so it's probably cheaper to |
| 85 | // pass it by value than by const reference. |
| 86 | |
| 87 | namespace impl { |
| 88 | |
| 89 | // Magic constant for indicating that the size of an ArrayView is variable |
| 90 | // instead of fixed. |
| 91 | enum : std::ptrdiff_t { kArrayViewVarSize = -4711 }; |
| 92 | |
| 93 | // Base class for ArrayViews of fixed nonzero size. |
| 94 | template <typename T, std::ptrdiff_t Size> |
| 95 | class ArrayViewBase { |
| 96 | static_assert(Size > 0, "ArrayView size must be variable or non-negative"); |
| 97 | |
| 98 | public: |
| 99 | ArrayViewBase(T* data, size_t size) : data_(data) {} |
| 100 | |
| 101 | static constexpr size_t size() { return Size; } |
| 102 | static constexpr bool empty() { return false; } |
| 103 | T* data() const { return data_; } |
| 104 | |
| 105 | protected: |
| 106 | static constexpr bool fixed_size() { return true; } |
| 107 | |
| 108 | private: |
| 109 | T* data_; |
| 110 | }; |
| 111 | |
| 112 | // Specialized base class for ArrayViews of fixed zero size. |
| 113 | template <typename T> |
| 114 | class ArrayViewBase<T, 0> { |
| 115 | public: |
| 116 | explicit ArrayViewBase(T* data, size_t size) {} |
| 117 | |
| 118 | static constexpr size_t size() { return 0; } |
| 119 | static constexpr bool empty() { return true; } |
| 120 | T* data() const { return nullptr; } |
| 121 | |
| 122 | protected: |
| 123 | static constexpr bool fixed_size() { return true; } |
| 124 | }; |
| 125 | |
| 126 | // Specialized base class for ArrayViews of variable size. |
| 127 | template <typename T> |
| 128 | class ArrayViewBase<T, impl::kArrayViewVarSize> { |
| 129 | public: |
| 130 | ArrayViewBase(T* data, size_t size) |
| 131 | : data_(size == 0 ? nullptr : data), size_(size) {} |
| 132 | |
| 133 | size_t size() const { return size_; } |
| 134 | bool empty() const { return size_ == 0; } |
| 135 | T* data() const { return data_; } |
| 136 | |
| 137 | protected: |
| 138 | static constexpr bool fixed_size() { return false; } |
| 139 | |
| 140 | private: |
| 141 | T* data_; |
| 142 | size_t size_; |
| 143 | }; |
| 144 | |
| 145 | } // namespace impl |
| 146 | |
| 147 | template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize> |
| 148 | class ArrayView final : public impl::ArrayViewBase<T, Size> { |
| 149 | public: |
| 150 | using value_type = T; |
| 151 | using const_iterator = const T*; |
| 152 | |
| 153 | // Construct an ArrayView from a pointer and a length. |
| 154 | template <typename U> |
| 155 | ArrayView(U* data, size_t size) |
| 156 | : impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) { |
| 157 | RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data()); |
| 158 | RTC_DCHECK_EQ(size, this->size()); |
| 159 | RTC_DCHECK_EQ(!this->data(), |
| 160 | this->size() == 0); // data is null iff size == 0. |
| 161 | } |
| 162 | |
| 163 | // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0 |
| 164 | // cannot be empty. |
| 165 | ArrayView() : ArrayView(nullptr, 0) {} |
| 166 | ArrayView(std::nullptr_t) // NOLINT |
| 167 | : ArrayView() {} |
| 168 | ArrayView(std::nullptr_t, size_t size) |
| 169 | : ArrayView(static_cast<T*>(nullptr), size) { |
| 170 | static_assert(Size == 0 || Size == impl::kArrayViewVarSize, ""); |
| 171 | RTC_DCHECK_EQ(0, size); |
| 172 | } |
| 173 | |
Alessio Bazzica | 858c4d7 | 2018-05-14 14:33:58 | [diff] [blame] | 174 | // Construct an ArrayView from a C-style array. |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 175 | template <typename U, size_t N> |
| 176 | ArrayView(U (&array)[N]) // NOLINT |
| 177 | : ArrayView(array, N) { |
| 178 | static_assert(Size == N || Size == impl::kArrayViewVarSize, |
| 179 | "Array size must match ArrayView size"); |
| 180 | } |
| 181 | |
Alessio Bazzica | 28a325b | 2018-05-15 12:57:51 | [diff] [blame] | 182 | // (Only if size is fixed.) Construct a fixed size ArrayView<T, N> from a |
| 183 | // non-const std::array instance. For an ArrayView with variable size, the |
| 184 | // used ctor is ArrayView(U& u) instead. |
Alessio Bazzica | 858c4d7 | 2018-05-14 14:33:58 | [diff] [blame] | 185 | template <typename U, |
| 186 | size_t N, |
| 187 | typename std::enable_if< |
| 188 | Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr> |
| 189 | ArrayView(std::array<U, N>& u) // NOLINT |
| 190 | : ArrayView(u.data(), u.size()) {} |
| 191 | |
Alessio Bazzica | 28a325b | 2018-05-15 12:57:51 | [diff] [blame] | 192 | // (Only if size is fixed.) Construct a fixed size ArrayView<T, N> where T is |
| 193 | // const from a const(expr) std::array instance. For an ArrayView with |
| 194 | // variable size, the used ctor is ArrayView(U& u) instead. |
| 195 | template <typename U, |
| 196 | size_t N, |
| 197 | typename std::enable_if< |
| 198 | Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr> |
| 199 | ArrayView(const std::array<U, N>& u) // NOLINT |
| 200 | : ArrayView(u.data(), u.size()) {} |
| 201 | |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 202 | // (Only if size is fixed.) Construct an ArrayView from any type U that has a |
| 203 | // static constexpr size() method whose return value is equal to Size, and a |
| 204 | // data() method whose return value converts implicitly to T*. In particular, |
| 205 | // this means we allow conversion from ArrayView<T, N> to ArrayView<const T, |
| 206 | // N>, but not the other way around. We also don't allow conversion from |
| 207 | // ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T, |
| 208 | // N> when M != N. |
| 209 | template < |
| 210 | typename U, |
| 211 | typename std::enable_if<Size != impl::kArrayViewVarSize && |
| 212 | HasDataAndSize<U, T>::value>::type* = nullptr> |
| 213 | ArrayView(U& u) // NOLINT |
| 214 | : ArrayView(u.data(), u.size()) { |
| 215 | static_assert(U::size() == Size, "Sizes must match exactly"); |
| 216 | } |
Karl Wiberg | ff61f3a | 2020-02-28 09:01:18 | [diff] [blame] | 217 | template < |
| 218 | typename U, |
| 219 | typename std::enable_if<Size != impl::kArrayViewVarSize && |
| 220 | HasDataAndSize<U, T>::value>::type* = nullptr> |
| 221 | ArrayView(const U& u) // NOLINT(runtime/explicit) |
| 222 | : ArrayView(u.data(), u.size()) { |
| 223 | static_assert(U::size() == Size, "Sizes must match exactly"); |
| 224 | } |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 225 | |
| 226 | // (Only if size is variable.) Construct an ArrayView from any type U that |
| 227 | // has a size() method whose return value converts implicitly to size_t, and |
| 228 | // a data() method whose return value converts implicitly to T*. In |
| 229 | // particular, this means we allow conversion from ArrayView<T> to |
| 230 | // ArrayView<const T>, but not the other way around. Other allowed |
| 231 | // conversions include |
| 232 | // ArrayView<T, N> to ArrayView<T> or ArrayView<const T>, |
| 233 | // std::vector<T> to ArrayView<T> or ArrayView<const T>, |
| 234 | // const std::vector<T> to ArrayView<const T>, |
| 235 | // rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and |
| 236 | // const rtc::Buffer to ArrayView<const uint8_t>. |
| 237 | template < |
| 238 | typename U, |
| 239 | typename std::enable_if<Size == impl::kArrayViewVarSize && |
| 240 | HasDataAndSize<U, T>::value>::type* = nullptr> |
| 241 | ArrayView(U& u) // NOLINT |
| 242 | : ArrayView(u.data(), u.size()) {} |
Karl Wiberg | 30abc36 | 2019-02-04 12:07:18 | [diff] [blame] | 243 | template < |
| 244 | typename U, |
| 245 | typename std::enable_if<Size == impl::kArrayViewVarSize && |
| 246 | HasDataAndSize<U, T>::value>::type* = nullptr> |
| 247 | ArrayView(const U& u) // NOLINT(runtime/explicit) |
| 248 | : ArrayView(u.data(), u.size()) {} |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 249 | |
| 250 | // Indexing and iteration. These allow mutation even if the ArrayView is |
| 251 | // const, because the ArrayView doesn't own the array. (To prevent mutation, |
| 252 | // use a const element type.) |
| 253 | T& operator[](size_t idx) const { |
| 254 | RTC_DCHECK_LT(idx, this->size()); |
| 255 | RTC_DCHECK(this->data()); |
| 256 | return this->data()[idx]; |
| 257 | } |
| 258 | T* begin() const { return this->data(); } |
| 259 | T* end() const { return this->data() + this->size(); } |
| 260 | const T* cbegin() const { return this->data(); } |
| 261 | const T* cend() const { return this->data() + this->size(); } |
Alessio Bazzica | e2c5485 | 2020-10-20 15:24:55 | [diff] [blame] | 262 | std::reverse_iterator<T*> rbegin() const { |
| 263 | return std::make_reverse_iterator(end()); |
| 264 | } |
| 265 | std::reverse_iterator<T*> rend() const { |
| 266 | return std::make_reverse_iterator(begin()); |
| 267 | } |
| 268 | std::reverse_iterator<const T*> crbegin() const { |
| 269 | return std::make_reverse_iterator(cend()); |
| 270 | } |
| 271 | std::reverse_iterator<const T*> crend() const { |
| 272 | return std::make_reverse_iterator(cbegin()); |
| 273 | } |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 274 | |
| 275 | ArrayView<T> subview(size_t offset, size_t size) const { |
| 276 | return offset < this->size() |
| 277 | ? ArrayView<T>(this->data() + offset, |
| 278 | std::min(size, this->size() - offset)) |
| 279 | : ArrayView<T>(); |
| 280 | } |
| 281 | ArrayView<T> subview(size_t offset) const { |
| 282 | return subview(offset, this->size()); |
| 283 | } |
| 284 | }; |
| 285 | |
| 286 | // Comparing two ArrayViews compares their (pointer,size) pairs; it does *not* |
| 287 | // dereference the pointers. |
| 288 | template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2> |
| 289 | bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) { |
| 290 | return a.data() == b.data() && a.size() == b.size(); |
| 291 | } |
| 292 | template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2> |
| 293 | bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) { |
| 294 | return !(a == b); |
| 295 | } |
| 296 | |
| 297 | // Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews |
| 298 | // are the size of one pointer. (And as a special case, fixed-size ArrayViews |
| 299 | // of size 0 require no storage.) |
| 300 | static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), ""); |
| 301 | static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), ""); |
| 302 | static_assert(std::is_empty<ArrayView<int, 0>>::value, ""); |
| 303 | |
| 304 | template <typename T> |
| 305 | inline ArrayView<T> MakeArrayView(T* data, size_t size) { |
| 306 | return ArrayView<T>(data, size); |
| 307 | } |
| 308 | |
Amit Hilbuch | e155dd0 | 2019-03-22 17:16:07 | [diff] [blame] | 309 | // Only for primitive types that have the same size and aligment. |
| 310 | // Allow reinterpret cast of the array view to another primitive type of the |
| 311 | // same size. |
| 312 | // Template arguments order is (U, T, Size) to allow deduction of the template |
| 313 | // arguments in client calls: reinterpret_array_view<target_type>(array_view). |
| 314 | template <typename U, typename T, std::ptrdiff_t Size> |
| 315 | inline ArrayView<U, Size> reinterpret_array_view(ArrayView<T, Size> view) { |
| 316 | static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T), |
| 317 | "ArrayView reinterpret_cast is only supported for casting " |
| 318 | "between views that represent the same chunk of memory."); |
| 319 | static_assert( |
| 320 | std::is_fundamental<T>::value && std::is_fundamental<U>::value, |
| 321 | "ArrayView reinterpret_cast is only supported for casting between " |
| 322 | "fundamental types."); |
| 323 | return ArrayView<U, Size>(reinterpret_cast<U*>(view.data()), view.size()); |
| 324 | } |
| 325 | |
kwiberg | 529662a | 2017-09-04 12:43:17 | [diff] [blame] | 326 | } // namespace rtc |
| 327 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 328 | #endif // API_ARRAY_VIEW_H_ |