OLD | NEW |
---|---|
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 Loading... | |
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"); | |
ossu
2017/03/01 23:16:00
Why can't the converted-to size be <= the converte
kwiberg-webrtc
2017/03/02 03:00:39
Well, it *could*, but IMO what you want for fixed-
ossu
2017/03/02 17:54:07
Yeah, maybe having it implicit is too magical. I'm
kwiberg-webrtc
2017/03/02 19:22:41
Yes, a subview() with only template arguments was
| |
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 { |
ossu
2017/03/01 23:16:00
I guess this is the way to convert to a smaller Ar
kwiberg-webrtc
2017/03/02 03:00:39
Yes, it can be argued both ways. But this was the
ossu
2017/03/02 17:54:07
Alright. SGTM!
| |
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_ |
OLD | NEW |