Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(321)

Side by Side Diff: webrtc/base/array_view.h

Issue 2667383006: ArrayView: Support compile-time constant sizes (Closed)
Patch Set: Add missing "c"s Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | webrtc/base/array_view_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 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 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 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
(...skipping 13 matching lines...) Expand all
24 // if (arr[i] == 17) 24 // if (arr[i] == 17)
25 // return true; 25 // return true;
26 // } 26 // }
27 // return false; 27 // return false;
28 // } 28 // }
29 // 29 //
30 // This is flexible, since it doesn't matter how the array is stored (C array, 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 31 // std::vector, rtc::Buffer, ...), but it's error-prone because the caller has
32 // to correctly specify the array length: 32 // to correctly specify the array length:
33 // 33 //
34 // Contains17(arr, arraysize(arr)); // C array 34 // Contains17(arr, arraysize(arr)); // C array
35 // Contains17(&arr[0], arr.size()); // std::vector 35 // Contains17(arr.data(), arr.size()); // std::vector
36 // Contains17(arr, size); // pointer + size 36 // Contains17(arr, size); // pointer + size
37 // ... 37 // ...
38 // 38 //
39 // It's also kind of messy to have two separate arguments for what is 39 // It's also kind of messy to have two separate arguments for what is
40 // conceptually a single thing. 40 // conceptually a single thing.
41 // 41 //
42 // Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't 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 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: 44 // indexing and iteration. It allows us to write our function like this:
45 // 45 //
46 // bool Contains17(rtc::ArrayView<const int> arr) { 46 // bool Contains17(rtc::ArrayView<const int> arr) {
47 // for (auto e : arr) { 47 // for (auto e : arr) {
48 // if (e == 17) 48 // if (e == 17)
49 // return true; 49 // return true;
50 // } 50 // }
51 // return false; 51 // return false;
52 // } 52 // }
53 // 53 //
54 // And even better, because a bunch of things will implicitly convert to 54 // And even better, because a bunch of things will implicitly convert to
55 // ArrayView, we can call it like this: 55 // ArrayView, we can call it like this:
56 // 56 //
57 // Contains17(arr); // C array 57 // Contains17(arr); // C array
58 // Contains17(arr); // std::vector 58 // Contains17(arr); // std::vector
59 // Contains17(rtc::ArrayView<int>(arr, size)); // pointer + size 59 // Contains17(rtc::ArrayView<int>(arr, size)); // pointer + size
60 // Contains17(nullptr); // nullptr -> empty ArrayView 60 // Contains17(nullptr); // nullptr -> empty ArrayView
61 // ... 61 // ...
62 // 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 //
63 // One important point is that ArrayView<T> and ArrayView<const T> are 67 // One important point is that ArrayView<T> and ArrayView<const T> are
64 // different types, which allow and don't allow mutation of the array elements, 68 // different types, which allow and don't allow mutation of the array elements,
65 // respectively. The implicit conversions work just like you'd hope, so that 69 // respectively. The implicit conversions work just like you'd hope, so that
66 // e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const 70 // e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const
67 // int>, but const vector<int> will convert only to ArrayView<const int>. 71 // int>, but const vector<int> will convert only to ArrayView<const int>.
68 // (ArrayView itself can be the source type in such conversions, so 72 // (ArrayView itself can be the source type in such conversions, so
69 // ArrayView<int> will convert to ArrayView<const int>.) 73 // ArrayView<int> will convert to ArrayView<const int>.)
70 // 74 //
71 // Note: ArrayView is tiny (just a pointer and a count) and trivially copyable, 75 // Note: ArrayView is tiny (just a pointer and a count if variable-sized, just
72 // so it's probably cheaper to pass it by value than by const reference. 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
79 namespace impl {
80
81 // Magic constant for indicating that the size of an ArrayView is variable
82 // instead of fixed.
83 enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
84
85 // Base class for ArrayViews of fixed nonzero size.
86 template <typename T, std::ptrdiff_t Size>
87 class 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.
73 template <typename T> 105 template <typename T>
74 class ArrayView final { 106 class 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.
119 template <typename T>
120 class 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
139 template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
140 class ArrayView final : public impl::ArrayViewBase<T, Size> {
75 public: 141 public:
76 using value_type = T; 142 using value_type = T;
77 using const_iterator = const T*; 143 using const_iterator = const T*;
78 144
79 // Construct an empty ArrayView. 145 // Construct an ArrayView from a pointer and a length.
80 ArrayView() : ArrayView(static_cast<T*>(nullptr), 0) {}
81 ArrayView(std::nullptr_t) : ArrayView() {}
82
83 // Construct an ArrayView for a (pointer,size) pair.
84 template <typename U> 146 template <typename U>
85 ArrayView(U* data, size_t size) 147 ArrayView(U* data, size_t size)
86 : data_(size == 0 ? nullptr : data), size_(size) { 148 : impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
87 CheckInvariant(); 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.
88 } 153 }
89 154
90 // Construct an ArrayView for an array. 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.
91 template <typename U, size_t N> 166 template <typename U, size_t N>
92 ArrayView(U (&array)[N]) : ArrayView(&array[0], 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 }
93 171
94 // Construct an ArrayView for any type U that has a size() method whose 172 // (Only if size is fixed.) Construct an ArrayView from any type U that has a
95 // return value converts implicitly to size_t, and a data() method whose 173 // static constexpr size() method whose return value is equal to Size, and a
96 // return value converts implicitly to T*. In particular, this means we allow 174 // data() method whose return value converts implicitly to T*. In particular,
97 // conversion from ArrayView<T> to ArrayView<const T>, but not the other way 175 // this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
98 // around. Other allowed conversions include std::vector<T> to ArrayView<T> 176 // N>, but not the other way around. We also don't allow conversion from
99 // or ArrayView<const T>, const std::vector<T> to ArrayView<const T>, and 177 // ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
100 // rtc::Buffer to ArrayView<uint8_t> (with the same const behavior as 178 // N> when M != N.
101 // std::vector). 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>.
102 template < 198 template <
103 typename U, 199 typename U,
104 typename std::enable_if<HasDataAndSize<U, T>::value>::type* = nullptr> 200 typename std::enable_if<Size == impl::kArrayViewVarSize &&
201 HasDataAndSize<U, T>::value>::type* = nullptr>
105 ArrayView(U& u) : ArrayView(u.data(), u.size()) {} 202 ArrayView(U& u) : ArrayView(u.data(), u.size()) {}
106 203
107 // Indexing, size, and iteration. These allow mutation even if the ArrayView 204 // Indexing and iteration. These allow mutation even if the ArrayView is
108 // is const, because the ArrayView doesn't own the array. (To prevent 205 // const, because the ArrayView doesn't own the array. (To prevent mutation,
109 // mutation, use ArrayView<const T>.) 206 // use a const element type.)
110 size_t size() const { return size_; }
111 bool empty() const { return size_ == 0; }
112 T* data() const { return data_; }
113 T& operator[](size_t idx) const { 207 T& operator[](size_t idx) const {
114 RTC_DCHECK_LT(idx, size_); 208 RTC_DCHECK_LT(idx, this->size());
115 RTC_DCHECK(data_); // Follows from size_ > idx and the class invariant. 209 RTC_DCHECK(this->data());
116 return data_[idx]; 210 return this->data()[idx];
117 } 211 }
118 T* begin() const { return data_; } 212 T* begin() const { return this->data(); }
119 T* end() const { return data_ + size_; } 213 T* end() const { return this->data() + this->size(); }
120 const T* cbegin() const { return data_; } 214 const T* cbegin() const { return this->data(); }
121 const T* cend() const { return data_ + size_; } 215 const T* cend() const { return this->data() + this->size(); }
122 216
123 ArrayView subview(size_t offset, size_t size) const { 217 ArrayView<T> subview(size_t offset, size_t size) const {
124 if (offset >= size_) 218 return offset < this->size()
125 return ArrayView(); 219 ? ArrayView<T>(this->data() + offset,
126 return ArrayView(data_ + offset, std::min(size, size_ - offset)); 220 std::min(size, this->size() - offset))
221 : ArrayView<T>();
127 } 222 }
128 ArrayView subview(size_t offset) const { return subview(offset, size_); } 223 ArrayView<T> subview(size_t offset) const {
224 return subview(offset, this->size());
225 }
226 };
129 227
130 // Comparing two ArrayViews compares their (pointer,size) pairs; it does 228 // Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
131 // *not* dereference the pointers. 229 // dereference the pointers.
132 friend bool operator==(const ArrayView& a, const ArrayView& b) { 230 template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
133 return a.data_ == b.data_ && a.size_ == b.size_; 231 bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
134 } 232 return a.data() == b.data() && a.size() == b.size();
135 friend bool operator!=(const ArrayView& a, const ArrayView& b) { 233 }
136 return !(a == b); 234 template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
137 } 235 bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
236 return !(a == b);
237 }
138 238
139 private: 239 // Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
140 // Invariant: !data_ iff size_ == 0. 240 // are the size of one pointer. (And as a special case, fixed-size ArrayViews
141 void CheckInvariant() const { RTC_DCHECK_EQ(!data_, size_ == 0); } 241 // of size 0 require no storage.)
142 T* data_; 242 static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
143 size_t size_; 243 static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
144 }; 244 static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
145 245
146 template <typename T> 246 template <typename T>
147 inline ArrayView<T> MakeArrayView(T* data, size_t size) { 247 inline ArrayView<T> MakeArrayView(T* data, size_t size) {
148 return ArrayView<T>(data, size); 248 return ArrayView<T>(data, size);
149 } 249 }
150 250
151 } // namespace rtc 251 } // namespace rtc
152 252
153 #endif // WEBRTC_BASE_ARRAY_VIEW_H_ 253 #endif // WEBRTC_BASE_ARRAY_VIEW_H_
OLDNEW
« no previous file with comments | « no previous file | webrtc/base/array_view_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698