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

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

Issue 1929903002: Define rtc::BufferT, like rtc::Buffer but for any trivial type (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: review changes Created 4 years, 7 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 | « webrtc/base/base.gyp ('k') | webrtc/base/buffer.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 2004 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2004 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
11 #ifndef WEBRTC_BASE_BUFFER_H_ 11 #ifndef WEBRTC_BASE_BUFFER_H_
12 #define WEBRTC_BASE_BUFFER_H_ 12 #define WEBRTC_BASE_BUFFER_H_
13 13
14 #include <cstring> 14 #include <cstring>
15 #include <memory> 15 #include <memory>
16 #include <type_traits>
16 #include <utility> 17 #include <utility>
17 18
18 #include "webrtc/base/array_view.h" 19 #include "webrtc/base/array_view.h"
19 #include "webrtc/base/checks.h" 20 #include "webrtc/base/checks.h"
20 #include "webrtc/base/constructormagic.h"
21 21
22 namespace rtc { 22 namespace rtc {
23 23
24 namespace internal { 24 namespace internal {
25 25
26 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T 26 // (Internal; please don't use outside this file.) Determines if elements of
27 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like 27 // type U are compatible with a BufferT<T>. For most types, we just ignore
28 // this: 28 // top-level const and forbid top-level volatile and require T and U to be
29 // 29 // otherwise equal, but all byte-sized integers (notably char, int8_t, and
30 // template <typename T, typename ByteType<T>::t = 0> 30 // uint8_t) are compatible with each other. (Note: We aim to get rid of this
31 // void foo(T* x); 31 // behavior, and treat all types the same.)
32 // 32 template <typename T, typename U>
33 // to let foo<T> be defined only for byte-sized integers. 33 struct BufferCompat {
34 template <typename T> 34 static constexpr bool value =
35 struct ByteType { 35 !std::is_volatile<U>::value &&
36 private: 36 ((std::is_integral<T>::value && sizeof(T) == 1)
37 static int F(uint8_t*); 37 ? (std::is_integral<U>::value && sizeof(U) == 1)
38 static int F(int8_t*); 38 : (std::is_same<T, typename std::remove_const<U>::type>::value));
39 static int F(char*);
40
41 public:
42 using t = decltype(F(static_cast<T*>(nullptr)));
43 }; 39 };
44 40
45 } // namespace internal 41 } // namespace internal
46 42
47 // Basic buffer class, can be grown and shrunk dynamically. 43 // Basic buffer class, can be grown and shrunk dynamically.
48 // Unlike std::string/vector, does not initialize data when expanding capacity. 44 // Unlike std::string/vector, does not initialize data when increasing size.
49 class Buffer { 45 template <typename T>
46 class BufferT {
47 // We want T's destructor and default constructor to be trivial, i.e. perform
48 // no action, so that we don't have to touch the memory we allocate and
49 // deallocate. And we want T to be trivially copyable, so that we can copy T
50 // instances with std::memcpy. This is precisely the definition of a trivial
51 // type.
52 static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
53
54 // This class relies heavily on being able to mutate its data.
55 static_assert(!std::is_const<T>::value, "T may not be const");
56
50 public: 57 public:
51 Buffer(); // An empty buffer. 58 // An empty BufferT.
52 Buffer(Buffer&& buf); // Move contents from an existing buffer. 59 BufferT() : size_(0), capacity_(0), data_(nullptr) {
60 RTC_DCHECK(IsConsistent());
61 }
53 62
54 // Construct a buffer with the specified number of uninitialized bytes. 63 // Disable copy construction and copy assignment, since copying a buffer is
55 explicit Buffer(size_t size); 64 // expensive enough that we want to force the user to be explicit about it.
56 Buffer(size_t size, size_t capacity); 65 BufferT(const BufferT&) = delete;
66 BufferT& operator=(const BufferT&) = delete;
57 67
58 // Construct a buffer and copy the specified number of bytes into it. The 68 BufferT(BufferT&& buf)
59 // source array may be (const) uint8_t*, int8_t*, or char*. 69 : size_(buf.size()),
60 template <typename T, typename internal::ByteType<T>::t = 0> 70 capacity_(buf.capacity()),
61 Buffer(const T* data, size_t size) 71 data_(std::move(buf.data_)) {
62 : Buffer(data, size, size) {} 72 RTC_DCHECK(IsConsistent());
73 buf.OnMovedFrom();
74 }
63 75
64 template <typename T, typename internal::ByteType<T>::t = 0> 76 // Construct a buffer with the specified number of uninitialized elements.
65 Buffer(const T* data, size_t size, size_t capacity) 77 explicit BufferT(size_t size) : BufferT(size, size) {}
66 : Buffer(size, capacity) { 78
67 std::memcpy(data_.get(), data, size); 79 BufferT(size_t size, size_t capacity)
80 : size_(size),
81 capacity_(std::max(size, capacity)),
82 data_(new T[capacity_]) {
83 RTC_DCHECK(IsConsistent());
84 }
85
86 // Construct a buffer and copy the specified number of elements into it.
87 template <typename U,
88 typename std::enable_if<
89 internal::BufferCompat<T, U>::value>::type* = nullptr>
90 BufferT(const U* data, size_t size) : BufferT(data, size, size) {}
91
92 template <typename U,
93 typename std::enable_if<
94 internal::BufferCompat<T, U>::value>::type* = nullptr>
95 BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) {
96 static_assert(sizeof(T) == sizeof(U), "");
97 std::memcpy(data_.get(), data, size * sizeof(U));
68 } 98 }
69 99
70 // Construct a buffer from the contents of an array. 100 // Construct a buffer from the contents of an array.
71 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 101 template <typename U,
72 Buffer(const T(&array)[N]) 102 size_t N,
73 : Buffer(array, N) {} 103 typename std::enable_if<
104 internal::BufferCompat<T, U>::value>::type* = nullptr>
105 BufferT(U (&array)[N]) : BufferT(array, N) {}
74 106
75 ~Buffer(); 107 // Get a pointer to the data. Just .data() will give you a (const) T*, but if
76 108 // T is a byte-sized integer, you may also use .data<U>() for any other
77 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, 109 // byte-sized integer U.
78 // but you may also use .data<int8_t>() and .data<char>(). 110 template <typename U = T,
79 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> 111 typename std::enable_if<
80 const T* data() const { 112 internal::BufferCompat<T, U>::value>::type* = nullptr>
113 const U* data() const {
81 RTC_DCHECK(IsConsistent()); 114 RTC_DCHECK(IsConsistent());
82 return reinterpret_cast<T*>(data_.get()); 115 return reinterpret_cast<U*>(data_.get());
83 } 116 }
84 117
85 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> 118 template <typename U = T,
86 T* data() { 119 typename std::enable_if<
120 internal::BufferCompat<T, U>::value>::type* = nullptr>
121 U* data() {
87 RTC_DCHECK(IsConsistent()); 122 RTC_DCHECK(IsConsistent());
88 return reinterpret_cast<T*>(data_.get()); 123 return reinterpret_cast<U*>(data_.get());
89 } 124 }
90 125
91 size_t size() const { 126 size_t size() const {
92 RTC_DCHECK(IsConsistent()); 127 RTC_DCHECK(IsConsistent());
93 return size_; 128 return size_;
94 } 129 }
95 130
96 size_t capacity() const { 131 size_t capacity() const {
97 RTC_DCHECK(IsConsistent()); 132 RTC_DCHECK(IsConsistent());
98 return capacity_; 133 return capacity_;
99 } 134 }
100 135
101 Buffer& operator=(Buffer&& buf) { 136 BufferT& operator=(BufferT&& buf) {
102 RTC_DCHECK(IsConsistent()); 137 RTC_DCHECK(IsConsistent());
103 RTC_DCHECK(buf.IsConsistent()); 138 RTC_DCHECK(buf.IsConsistent());
104 size_ = buf.size_; 139 size_ = buf.size_;
105 capacity_ = buf.capacity_; 140 capacity_ = buf.capacity_;
106 data_ = std::move(buf.data_); 141 data_ = std::move(buf.data_);
107 buf.OnMovedFrom(); 142 buf.OnMovedFrom();
108 return *this; 143 return *this;
109 } 144 }
110 145
111 bool operator==(const Buffer& buf) const { 146 bool operator==(const BufferT& buf) const {
112 RTC_DCHECK(IsConsistent()); 147 RTC_DCHECK(IsConsistent());
113 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0; 148 if (size_ != buf.size_) {
149 return false;
150 }
151 if (std::is_integral<T>::value) {
152 // Optimization.
153 return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0;
154 }
155 for (size_t i = 0; i < size_; ++i) {
156 if (data_[i] != buf.data_[i]) {
157 return false;
158 }
159 }
160 return true;
114 } 161 }
115 162
116 bool operator!=(const Buffer& buf) const { return !(*this == buf); } 163 bool operator!=(const BufferT& buf) const { return !(*this == buf); }
117 164
118 uint8_t& operator[](size_t index) { 165 T& operator[](size_t index) {
119 RTC_DCHECK_LT(index, size_); 166 RTC_DCHECK_LT(index, size_);
120 return data()[index]; 167 return data()[index];
121 } 168 }
122 169
123 uint8_t operator[](size_t index) const { 170 T operator[](size_t index) const {
124 RTC_DCHECK_LT(index, size_); 171 RTC_DCHECK_LT(index, size_);
125 return data()[index]; 172 return data()[index];
126 } 173 }
127 174
128 // The SetData functions replace the contents of the buffer. They accept the 175 // The SetData functions replace the contents of the buffer. They accept the
129 // same input types as the constructors. 176 // same input types as the constructors.
130 template <typename T, typename internal::ByteType<T>::t = 0> 177 template <typename U,
131 void SetData(const T* data, size_t size) { 178 typename std::enable_if<
179 internal::BufferCompat<T, U>::value>::type* = nullptr>
180 void SetData(const U* data, size_t size) {
132 RTC_DCHECK(IsConsistent()); 181 RTC_DCHECK(IsConsistent());
133 size_ = 0; 182 size_ = 0;
134 AppendData(data, size); 183 AppendData(data, size);
135 } 184 }
136 185
137 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 186 template <typename U,
138 void SetData(const T(&array)[N]) { 187 size_t N,
188 typename std::enable_if<
189 internal::BufferCompat<T, U>::value>::type* = nullptr>
190 void SetData(const U (&array)[N]) {
139 SetData(array, N); 191 SetData(array, N);
140 } 192 }
141 193
142 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); } 194 void SetData(const BufferT& buf) { SetData(buf.data(), buf.size()); }
143 195
144 // Replace the data in the buffer with at most |max_bytes| of data, using the 196 // Replace the data in the buffer with at most |max_elements| of data, using
145 // function |setter|, which should have the following signature: 197 // the function |setter|, which should have the following signature:
146 // size_t setter(ArrayView<T> view) 198 // size_t setter(ArrayView<U> view)
147 // |setter| is given an appropriately typed ArrayView of the area in which to 199 // |setter| is given an appropriately typed ArrayView of the area in which to
148 // write the data (i.e. starting at the beginning of the buffer) and should 200 // write the data (i.e. starting at the beginning of the buffer) and should
149 // return the number of bytes actually written. This number must be <= 201 // return the number of elements actually written. This number must be <=
150 // |max_bytes|. 202 // |max_elements|.
151 template <typename T = uint8_t, typename F, 203 template <typename U = T,
152 typename internal::ByteType<T>::t = 0> 204 typename F,
153 size_t SetData(size_t max_bytes, F&& setter) { 205 typename std::enable_if<
206 internal::BufferCompat<T, U>::value>::type* = nullptr>
207 size_t SetData(size_t max_elements, F&& setter) {
154 RTC_DCHECK(IsConsistent()); 208 RTC_DCHECK(IsConsistent());
155 size_ = 0; 209 size_ = 0;
156 return AppendData<T>(max_bytes, std::forward<F>(setter)); 210 return AppendData<U>(max_elements, std::forward<F>(setter));
157 } 211 }
158 212
159 // The AppendData functions adds data to the end of the buffer. They accept 213 // The AppendData functions add data to the end of the buffer. They accept
160 // the same input types as the constructors. 214 // the same input types as the constructors.
161 template <typename T, typename internal::ByteType<T>::t = 0> 215 template <typename U,
162 void AppendData(const T* data, size_t size) { 216 typename std::enable_if<
217 internal::BufferCompat<T, U>::value>::type* = nullptr>
218 void AppendData(const U* data, size_t size) {
163 RTC_DCHECK(IsConsistent()); 219 RTC_DCHECK(IsConsistent());
164 const size_t new_size = size_ + size; 220 const size_t new_size = size_ + size;
165 EnsureCapacity(new_size); 221 EnsureCapacity(new_size);
166 std::memcpy(data_.get() + size_, data, size); 222 static_assert(sizeof(T) == sizeof(U), "");
223 std::memcpy(data_.get() + size_, data, size * sizeof(U));
167 size_ = new_size; 224 size_ = new_size;
168 RTC_DCHECK(IsConsistent()); 225 RTC_DCHECK(IsConsistent());
169 } 226 }
170 227
171 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 228 template <typename U,
172 void AppendData(const T(&array)[N]) { 229 size_t N,
230 typename std::enable_if<
231 internal::BufferCompat<T, U>::value>::type* = nullptr>
232 void AppendData(const U (&array)[N]) {
173 AppendData(array, N); 233 AppendData(array, N);
174 } 234 }
175 235
176 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } 236 void AppendData(const BufferT& buf) { AppendData(buf.data(), buf.size()); }
177 237
178 // Append at most |max_bytes| of data to the end of the buffer, using the 238 // Append at most |max_elements| to the end of the buffer, using the function
179 // function |setter|, which should have the following signature: 239 // |setter|, which should have the following signature:
180 // size_t setter(ArrayView<T> view) 240 // size_t setter(ArrayView<U> view)
181 // |setter| is given an appropriately typed ArrayView of the area in which to 241 // |setter| is given an appropriately typed ArrayView of the area in which to
182 // write the data (i.e. starting at the former end of the buffer) and should 242 // write the data (i.e. starting at the former end of the buffer) and should
183 // return the number of bytes actually written. This number must be <= 243 // return the number of elements actually written. This number must be <=
184 // |max_bytes|. 244 // |max_elements|.
185 template <typename T = uint8_t, typename F, 245 template <typename U = T,
186 typename internal::ByteType<T>::t = 0> 246 typename F,
187 size_t AppendData(size_t max_bytes, F&& setter) { 247 typename std::enable_if<
248 internal::BufferCompat<T, U>::value>::type* = nullptr>
249 size_t AppendData(size_t max_elements, F&& setter) {
188 RTC_DCHECK(IsConsistent()); 250 RTC_DCHECK(IsConsistent());
189 const size_t old_size = size_; 251 const size_t old_size = size_;
190 SetSize(old_size + max_bytes); 252 SetSize(old_size + max_elements);
191 T *base_ptr = data<T>() + old_size; 253 U* base_ptr = data<U>() + old_size;
192 size_t written_bytes = 254 size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements));
193 setter(rtc::ArrayView<T>(base_ptr, max_bytes));
194 255
195 RTC_CHECK_LE(written_bytes, max_bytes); 256 RTC_CHECK_LE(written_elements, max_elements);
196 size_ = old_size + written_bytes; 257 size_ = old_size + written_elements;
197 RTC_DCHECK(IsConsistent()); 258 RTC_DCHECK(IsConsistent());
198 return written_bytes; 259 return written_elements;
199 } 260 }
200 261
201 // Sets the size of the buffer. If the new size is smaller than the old, the 262 // Sets the size of the buffer. If the new size is smaller than the old, the
202 // buffer contents will be kept but truncated; if the new size is greater, 263 // buffer contents will be kept but truncated; if the new size is greater,
203 // the existing contents will be kept and the new space will be 264 // the existing contents will be kept and the new space will be
204 // uninitialized. 265 // uninitialized.
205 void SetSize(size_t size) { 266 void SetSize(size_t size) {
206 EnsureCapacity(size); 267 EnsureCapacity(size);
207 size_ = size; 268 size_ = size;
208 } 269 }
209 270
210 // Ensure that the buffer size can be increased to at least capacity without 271 // Ensure that the buffer size can be increased to at least capacity without
211 // further reallocation. (Of course, this operation might need to reallocate 272 // further reallocation. (Of course, this operation might need to reallocate
212 // the buffer.) 273 // the buffer.)
213 void EnsureCapacity(size_t capacity) { 274 void EnsureCapacity(size_t capacity) {
214 RTC_DCHECK(IsConsistent()); 275 RTC_DCHECK(IsConsistent());
215 if (capacity <= capacity_) 276 if (capacity <= capacity_)
216 return; 277 return;
217 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); 278 std::unique_ptr<T[]> new_data(new T[capacity]);
218 std::memcpy(new_data.get(), data_.get(), size_); 279 std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
219 data_ = std::move(new_data); 280 data_ = std::move(new_data);
220 capacity_ = capacity; 281 capacity_ = capacity;
221 RTC_DCHECK(IsConsistent()); 282 RTC_DCHECK(IsConsistent());
222 } 283 }
223 284
224 // Resets the buffer to zero size without altering capacity. Works even if the 285 // Resets the buffer to zero size without altering capacity. Works even if the
225 // buffer has been moved from. 286 // buffer has been moved from.
226 void Clear() { 287 void Clear() {
227 size_ = 0; 288 size_ = 0;
228 RTC_DCHECK(IsConsistent()); 289 RTC_DCHECK(IsConsistent());
229 } 290 }
230 291
231 // Swaps two buffers. Also works for buffers that have been moved from. 292 // Swaps two buffers. Also works for buffers that have been moved from.
232 friend void swap(Buffer& a, Buffer& b) { 293 friend void swap(BufferT& a, BufferT& b) {
233 using std::swap; 294 using std::swap;
234 swap(a.size_, b.size_); 295 swap(a.size_, b.size_);
235 swap(a.capacity_, b.capacity_); 296 swap(a.capacity_, b.capacity_);
236 swap(a.data_, b.data_); 297 swap(a.data_, b.data_);
237 } 298 }
238 299
239 private: 300 private:
240 // Precondition for all methods except Clear and the destructor. 301 // Precondition for all methods except Clear and the destructor.
241 // Postcondition for all methods except move construction and move 302 // Postcondition for all methods except move construction and move
242 // assignment, which leave the moved-from object in a possibly inconsistent 303 // assignment, which leave the moved-from object in a possibly inconsistent
(...skipping 12 matching lines...) Expand all
255 capacity_ = 0; 316 capacity_ = 0;
256 #else 317 #else
257 // Ensure that *this is always inconsistent, to provoke bugs. 318 // Ensure that *this is always inconsistent, to provoke bugs.
258 size_ = 1; 319 size_ = 1;
259 capacity_ = 0; 320 capacity_ = 0;
260 #endif 321 #endif
261 } 322 }
262 323
263 size_t size_; 324 size_t size_;
264 size_t capacity_; 325 size_t capacity_;
265 std::unique_ptr<uint8_t[]> data_; 326 std::unique_ptr<T[]> data_;
327 };
266 328
267 RTC_DISALLOW_COPY_AND_ASSIGN(Buffer); 329 // By far the most common sort of buffer.
268 }; 330 using Buffer = BufferT<uint8_t>;
269 331
270 } // namespace rtc 332 } // namespace rtc
271 333
272 #endif // WEBRTC_BASE_BUFFER_H_ 334 #endif // WEBRTC_BASE_BUFFER_H_
OLDNEW
« no previous file with comments | « webrtc/base/base.gyp ('k') | webrtc/base/buffer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698