blob: 0f0e3f379017d8a178fbfb8865056784d12fddd7 [file] [log] [blame]
Karl Wiberge2a83ee2015-10-26 19:51:29 +01001/*
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
Henrik Kjellanderc0362762017-06-29 08:03:04 +020011#ifndef WEBRTC_RTC_BASE_ARRAY_VIEW_H_
12#define WEBRTC_RTC_BASE_ARRAY_VIEW_H_
Karl Wiberge2a83ee2015-10-26 19:51:29 +010013
Henrik Kjellander00725112017-06-30 15:14:45 +020014#include "webrtc/base/checks.h"
15#include "webrtc/base/type_traits.h"
Karl Wiberge2a83ee2015-10-26 19:51:29 +010016
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020017namespace rtc {
18
19// Many functions read from or write to arrays. The obvious way to do this is
20// to use two arguments, a pointer to the first element and an element count:
21//
22// bool Contains17(const int* arr, size_t size) {
23// for (size_t i = 0; i < size; ++i) {
24// if (arr[i] == 17)
25// return true;
26// }
27// return false;
28// }
29//
30// This is flexible, since it doesn't matter how the array is stored (C array,
31// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has
32// to correctly specify the array length:
33//
34// Contains17(arr, arraysize(arr)); // C array
35// Contains17(arr.data(), arr.size()); // std::vector
36// Contains17(arr, size); // pointer + size
37// ...
38//
39// It's also kind of messy to have two separate arguments for what is
40// conceptually a single thing.
41//
42// Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't
43// own) and a count, and supports the basic things you'd expect, such as
44// indexing and iteration. It allows us to write our function like this:
45//
46// bool Contains17(rtc::ArrayView<const int> arr) {
47// for (auto e : arr) {
48// if (e == 17)
49// return true;
50// }
51// return false;
52// }
53//
54// And even better, because a bunch of things will implicitly convert to
55// ArrayView, we can call it like this:
56//
57// Contains17(arr); // C array
58// Contains17(arr); // std::vector
59// Contains17(rtc::ArrayView<int>(arr, size)); // pointer + size
60// Contains17(nullptr); // nullptr -> empty ArrayView
61// ...
62//
63// ArrayView<T> stores both a pointer and a size, but you may also use
64// ArrayView<T, N>, which has a size that's fixed at compile time (which means
65// it only has to store the pointer).
66//
67// One important point is that ArrayView<T> and ArrayView<const T> are
68// different types, which allow and don't allow mutation of the array elements,
69// respectively. The implicit conversions work just like you'd hope, so that
70// e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const
71// int>, but const vector<int> will convert only to ArrayView<const int>.
72// (ArrayView itself can be the source type in such conversions, so
73// ArrayView<int> will convert to ArrayView<const int>.)
74//
75// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just
76// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to
77// pass it by value than by const reference.
78
79namespace impl {
80
81// Magic constant for indicating that the size of an ArrayView is variable
82// instead of fixed.
83enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
84
85// Base class for ArrayViews of fixed nonzero size.
86template <typename T, std::ptrdiff_t Size>
87class ArrayViewBase {
88 static_assert(Size > 0, "ArrayView size must be variable or non-negative");
89
90 public:
91 ArrayViewBase(T* data, size_t size) : data_(data) {}
92
93 static constexpr size_t size() { return Size; }
94 static constexpr bool empty() { return false; }
95 T* data() const { return data_; }
96
97 protected:
98 static constexpr bool fixed_size() { return true; }
99
100 private:
101 T* data_;
102};
103
104// Specialized base class for ArrayViews of fixed zero size.
105template <typename T>
106class ArrayViewBase<T, 0> {
107 public:
108 explicit ArrayViewBase(T* data, size_t size) {}
109
110 static constexpr size_t size() { return 0; }
111 static constexpr bool empty() { return true; }
112 T* data() const { return nullptr; }
113
114 protected:
115 static constexpr bool fixed_size() { return true; }
116};
117
118// Specialized base class for ArrayViews of variable size.
119template <typename T>
120class ArrayViewBase<T, impl::kArrayViewVarSize> {
121 public:
122 ArrayViewBase(T* data, size_t size)
123 : data_(size == 0 ? nullptr : data), size_(size) {}
124
125 size_t size() const { return size_; }
126 bool empty() const { return size_ == 0; }
127 T* data() const { return data_; }
128
129 protected:
130 static constexpr bool fixed_size() { return false; }
131
132 private:
133 T* data_;
134 size_t size_;
135};
136
137} // namespace impl
138
139template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
140class ArrayView final : public impl::ArrayViewBase<T, Size> {
141 public:
142 using value_type = T;
143 using const_iterator = const T*;
144
145 // Construct an ArrayView from a pointer and a length.
146 template <typename U>
147 ArrayView(U* data, size_t size)
148 : impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
149 RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
150 RTC_DCHECK_EQ(size, this->size());
151 RTC_DCHECK_EQ(!this->data(),
152 this->size() == 0); // data is null iff size == 0.
153 }
154
155 // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
156 // cannot be empty.
157 ArrayView() : ArrayView(nullptr, 0) {}
158 ArrayView(std::nullptr_t) : ArrayView() {}
159 ArrayView(std::nullptr_t, size_t size)
160 : ArrayView(static_cast<T*>(nullptr), size) {
161 static_assert(Size == 0 || Size == impl::kArrayViewVarSize, "");
162 RTC_DCHECK_EQ(0, size);
163 }
164
165 // Construct an ArrayView from an array.
166 template <typename U, size_t N>
167 ArrayView(U (&array)[N]) : ArrayView(array, N) {
168 static_assert(Size == N || Size == impl::kArrayViewVarSize,
169 "Array size must match ArrayView size");
170 }
171
172 // (Only if size is fixed.) Construct an ArrayView from any type U that has a
173 // static constexpr size() method whose return value is equal to Size, and a
174 // data() method whose return value converts implicitly to T*. In particular,
175 // this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
176 // N>, but not the other way around. We also don't allow conversion from
177 // ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
178 // N> when M != N.
179 template <typename U,
180 typename std::enable_if<
181 Size != impl::kArrayViewVarSize &&
182 HasDataAndSize<U, T>::value>::type* = nullptr>
183 ArrayView(U& u) : ArrayView(u.data(), u.size()) {
184 static_assert(U::size() == Size, "Sizes must match exactly");
185 }
186
187 // (Only if size is variable.) Construct an ArrayView from any type U that
188 // has a size() method whose return value converts implicitly to size_t, and
189 // a data() method whose return value converts implicitly to T*. In
190 // particular, this means we allow conversion from ArrayView<T> to
191 // ArrayView<const T>, but not the other way around. Other allowed
192 // conversions include
193 // ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
194 // std::vector<T> to ArrayView<T> or ArrayView<const T>,
195 // const std::vector<T> to ArrayView<const T>,
196 // rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
197 // const rtc::Buffer to ArrayView<const uint8_t>.
198 template <
199 typename U,
200 typename std::enable_if<Size == impl::kArrayViewVarSize &&
201 HasDataAndSize<U, T>::value>::type* = nullptr>
202 ArrayView(U& u) : ArrayView(u.data(), u.size()) {}
203
204 // Indexing and iteration. These allow mutation even if the ArrayView is
205 // const, because the ArrayView doesn't own the array. (To prevent mutation,
206 // use a const element type.)
207 T& operator[](size_t idx) const {
208 RTC_DCHECK_LT(idx, this->size());
209 RTC_DCHECK(this->data());
210 return this->data()[idx];
211 }
212 T* begin() const { return this->data(); }
213 T* end() const { return this->data() + this->size(); }
214 const T* cbegin() const { return this->data(); }
215 const T* cend() const { return this->data() + this->size(); }
216
217 ArrayView<T> subview(size_t offset, size_t size) const {
218 return offset < this->size()
219 ? ArrayView<T>(this->data() + offset,
220 std::min(size, this->size() - offset))
221 : ArrayView<T>();
222 }
223 ArrayView<T> subview(size_t offset) const {
224 return subview(offset, this->size());
225 }
226};
227
228// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
229// dereference the pointers.
230template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
231bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
232 return a.data() == b.data() && a.size() == b.size();
233}
234template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
235bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
236 return !(a == b);
237}
238
239// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
240// are the size of one pointer. (And as a special case, fixed-size ArrayViews
241// of size 0 require no storage.)
242static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
243static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
244static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
245
246template <typename T>
247inline ArrayView<T> MakeArrayView(T* data, size_t size) {
248 return ArrayView<T>(data, size);
249}
250
251} // namespace rtc
Karl Wiberge2a83ee2015-10-26 19:51:29 +0100252
Henrik Kjellanderc0362762017-06-29 08:03:04 +0200253#endif // WEBRTC_RTC_BASE_ARRAY_VIEW_H_