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

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 nit 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.
31 // void foo(T* x); 31 template <typename T, typename U>
32 // 32 struct BufferCompat {
ossu 2016/04/29 09:09:55 So if we want to get away from allowing flexibilit
kwiberg-webrtc 2016/04/29 10:34:46 OK, that makes sense. Will do.
33 // to let foo<T> be defined only for byte-sized integers. 33 static constexpr bool value =
34 template <typename T> 34 !std::is_volatile<U>::value &&
35 struct ByteType { 35 ((std::is_integral<T>::value && sizeof(T) == 1)
36 private: 36 ? (std::is_integral<U>::value && sizeof(U) == 1)
37 static int F(uint8_t*); 37 : (std::is_same<T, typename std::remove_const<U>::type>::value));
38 static int F(int8_t*);
39 static int F(char*);
40
41 public:
42 using t = decltype(F(static_cast<T*>(nullptr)));
43 }; 38 };
44 39
45 } // namespace internal 40 } // namespace internal
46 41
47 // Basic buffer class, can be grown and shrunk dynamically. 42 // Basic buffer class, can be grown and shrunk dynamically.
48 // Unlike std::string/vector, does not initialize data when expanding capacity. 43 // Unlike std::string/vector, does not initialize data when increasing size.
49 class Buffer { 44 template <typename T>
45 class BufferT {
46 // We want T's destructor and default constructor to be trivial, i.e. perform
47 // no action, so that we don't have to touch the memory we allocate and
48 // deallocate. And we want T to be trivially copyable, so that we can copy T
49 // instances with std::memcpy. This is precisely the definition of a trivial
50 // type.
51 static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
52
53 // This class relies heavily on being able to mutate its data.
54 static_assert(!std::is_const<T>::value, "T may not be const");
55
50 public: 56 public:
51 Buffer(); // An empty buffer. 57 // An empty BufferT.
52 Buffer(Buffer&& buf); // Move contents from an existing buffer. 58 BufferT() : size_(0), capacity_(0), data_(nullptr) {
59 RTC_DCHECK(IsConsistent());
60 }
53 61
54 // Construct a buffer with the specified number of uninitialized bytes. 62 // Disable copy construction and copy assignment, since copying a buffer is
55 explicit Buffer(size_t size); 63 // expensive enough that we want to force the user to be explicit about it.
56 Buffer(size_t size, size_t capacity); 64 BufferT(const BufferT&) = delete;
65 BufferT& operator=(const BufferT&) = delete;
57 66
58 // Construct a buffer and copy the specified number of bytes into it. The 67 BufferT(BufferT&& buf)
59 // source array may be (const) uint8_t*, int8_t*, or char*. 68 : size_(buf.size()),
60 template <typename T, typename internal::ByteType<T>::t = 0> 69 capacity_(buf.capacity()),
61 Buffer(const T* data, size_t size) 70 data_(std::move(buf.data_)) {
62 : Buffer(data, size, size) {} 71 RTC_DCHECK(IsConsistent());
72 buf.OnMovedFrom();
73 }
63 74
64 template <typename T, typename internal::ByteType<T>::t = 0> 75 // Construct a buffer with the specified number of uninitialized elements.
65 Buffer(const T* data, size_t size, size_t capacity) 76 explicit BufferT(size_t size) : BufferT(size, size) {}
66 : Buffer(size, capacity) { 77
67 std::memcpy(data_.get(), data, size); 78 BufferT(size_t size, size_t capacity)
79 : size_(size),
80 capacity_(std::max(size, capacity)),
81 data_(new T[capacity_]) {
82 RTC_DCHECK(IsConsistent());
83 }
84
85 // Construct a buffer and copy the specified number of elements into it.
86 template <typename U,
87 typename std::enable_if<
88 internal::BufferCompat<T, U>::value>::type* = nullptr>
89 BufferT(const U* data, size_t size) : BufferT(data, size, size) {}
90
91 template <typename U,
92 typename std::enable_if<
93 internal::BufferCompat<T, U>::value>::type* = nullptr>
94 BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) {
95 static_assert(sizeof(T) == sizeof(U), "");
96 std::memcpy(data_.get(), data, size * sizeof(U));
68 } 97 }
69 98
70 // Construct a buffer from the contents of an array. 99 // Construct a buffer from the contents of an array.
71 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 100 template <typename U,
72 Buffer(const T(&array)[N]) 101 size_t N,
73 : Buffer(array, N) {} 102 typename std::enable_if<
103 internal::BufferCompat<T, U>::value>::type* = nullptr>
104 BufferT(U (&array)[N]) : BufferT(array, N) {}
74 105
75 ~Buffer(); 106 // Get a pointer to the data. Just .data() will give you a (const) T*, but if
76 107 // 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*, 108 // byte-sized integer U.
78 // but you may also use .data<int8_t>() and .data<char>(). 109 template <typename U = T,
79 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> 110 typename std::enable_if<
80 const T* data() const { 111 internal::BufferCompat<T, U>::value>::type* = nullptr>
112 const U* data() const {
81 RTC_DCHECK(IsConsistent()); 113 RTC_DCHECK(IsConsistent());
82 return reinterpret_cast<T*>(data_.get()); 114 return reinterpret_cast<U*>(data_.get());
83 } 115 }
84 116
85 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> 117 template <typename U = T,
86 T* data() { 118 typename std::enable_if<
119 internal::BufferCompat<T, U>::value>::type* = nullptr>
120 U* data() {
87 RTC_DCHECK(IsConsistent()); 121 RTC_DCHECK(IsConsistent());
88 return reinterpret_cast<T*>(data_.get()); 122 return reinterpret_cast<U*>(data_.get());
89 } 123 }
90 124
91 size_t size() const { 125 size_t size() const {
92 RTC_DCHECK(IsConsistent()); 126 RTC_DCHECK(IsConsistent());
93 return size_; 127 return size_;
94 } 128 }
95 129
96 size_t capacity() const { 130 size_t capacity() const {
97 RTC_DCHECK(IsConsistent()); 131 RTC_DCHECK(IsConsistent());
98 return capacity_; 132 return capacity_;
99 } 133 }
100 134
101 Buffer& operator=(Buffer&& buf) { 135 BufferT& operator=(BufferT&& buf) {
102 RTC_DCHECK(IsConsistent()); 136 RTC_DCHECK(IsConsistent());
103 RTC_DCHECK(buf.IsConsistent()); 137 RTC_DCHECK(buf.IsConsistent());
104 size_ = buf.size_; 138 size_ = buf.size_;
105 capacity_ = buf.capacity_; 139 capacity_ = buf.capacity_;
106 data_ = std::move(buf.data_); 140 data_ = std::move(buf.data_);
107 buf.OnMovedFrom(); 141 buf.OnMovedFrom();
108 return *this; 142 return *this;
109 } 143 }
110 144
111 bool operator==(const Buffer& buf) const { 145 bool operator==(const BufferT& buf) const {
ossu 2016/04/29 09:09:55 I was sure I'd asked about this but I can't seem t
kwiberg-webrtc 2016/04/29 10:34:46 Yes, a version of operator== that uses T's operato
146 static_assert(std::is_integral<T>::value, "T must be an integral type");
112 RTC_DCHECK(IsConsistent()); 147 RTC_DCHECK(IsConsistent());
113 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0; 148 return size_ == buf.size() &&
149 std::memcmp(data_.get(), buf.data(), size_ * sizeof(T)) == 0;
114 } 150 }
115 151
116 bool operator!=(const Buffer& buf) const { return !(*this == buf); } 152 bool operator!=(const BufferT& buf) const { return !(*this == buf); }
117 153
118 uint8_t& operator[](size_t index) { 154 T& operator[](size_t index) {
119 RTC_DCHECK_LT(index, size_); 155 RTC_DCHECK_LT(index, size_);
120 return data()[index]; 156 return data()[index];
121 } 157 }
122 158
123 uint8_t operator[](size_t index) const { 159 T operator[](size_t index) const {
124 RTC_DCHECK_LT(index, size_); 160 RTC_DCHECK_LT(index, size_);
125 return data()[index]; 161 return data()[index];
126 } 162 }
127 163
128 // The SetData functions replace the contents of the buffer. They accept the 164 // The SetData functions replace the contents of the buffer. They accept the
129 // same input types as the constructors. 165 // same input types as the constructors.
130 template <typename T, typename internal::ByteType<T>::t = 0> 166 template <typename U,
131 void SetData(const T* data, size_t size) { 167 typename std::enable_if<
168 internal::BufferCompat<T, U>::value>::type* = nullptr>
169 void SetData(const U* data, size_t size) {
132 RTC_DCHECK(IsConsistent()); 170 RTC_DCHECK(IsConsistent());
133 size_ = 0; 171 size_ = 0;
134 AppendData(data, size); 172 AppendData(data, size);
135 } 173 }
136 174
137 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 175 template <typename U,
138 void SetData(const T(&array)[N]) { 176 size_t N,
177 typename std::enable_if<
178 internal::BufferCompat<T, U>::value>::type* = nullptr>
179 void SetData(const U (&array)[N]) {
139 SetData(array, N); 180 SetData(array, N);
140 } 181 }
141 182
142 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); } 183 void SetData(const BufferT& buf) { SetData(buf.data(), buf.size()); }
143 184
144 // Replace the data in the buffer with at most |max_bytes| of data, using the 185 // Replace the data in the buffer with at most |max_elements| of data, using
145 // function |setter|, which should have the following signature: 186 // the function |setter|, which should have the following signature:
146 // size_t setter(ArrayView<T> view) 187 // size_t setter(ArrayView<U> view)
147 // |setter| is given an appropriately typed ArrayView of the area in which to 188 // |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 189 // 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 <= 190 // return the number of elements actually written. This number must be <=
150 // |max_bytes|. 191 // |max_elements|.
151 template <typename T = uint8_t, typename F, 192 template <typename U = T,
152 typename internal::ByteType<T>::t = 0> 193 typename F,
153 size_t SetData(size_t max_bytes, F&& setter) { 194 typename std::enable_if<
195 internal::BufferCompat<T, U>::value>::type* = nullptr>
196 size_t SetData(size_t max_elements, F&& setter) {
154 RTC_DCHECK(IsConsistent()); 197 RTC_DCHECK(IsConsistent());
155 size_ = 0; 198 size_ = 0;
156 return AppendData<T>(max_bytes, std::forward<F>(setter)); 199 return AppendData<U>(max_elements, std::forward<F>(setter));
157 } 200 }
158 201
159 // The AppendData functions adds data to the end of the buffer. They accept 202 // The AppendData functions add data to the end of the buffer. They accept
160 // the same input types as the constructors. 203 // the same input types as the constructors.
161 template <typename T, typename internal::ByteType<T>::t = 0> 204 template <typename U,
162 void AppendData(const T* data, size_t size) { 205 typename std::enable_if<
206 internal::BufferCompat<T, U>::value>::type* = nullptr>
207 void AppendData(const U* data, size_t size) {
163 RTC_DCHECK(IsConsistent()); 208 RTC_DCHECK(IsConsistent());
164 const size_t new_size = size_ + size; 209 const size_t new_size = size_ + size;
165 EnsureCapacity(new_size); 210 EnsureCapacity(new_size);
166 std::memcpy(data_.get() + size_, data, size); 211 static_assert(sizeof(T) == sizeof(U), "");
212 std::memcpy(data_.get() + size_, data, size * sizeof(U));
167 size_ = new_size; 213 size_ = new_size;
168 RTC_DCHECK(IsConsistent()); 214 RTC_DCHECK(IsConsistent());
169 } 215 }
170 216
171 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> 217 template <typename U,
172 void AppendData(const T(&array)[N]) { 218 size_t N,
219 typename std::enable_if<
220 internal::BufferCompat<T, U>::value>::type* = nullptr>
221 void AppendData(const U (&array)[N]) {
173 AppendData(array, N); 222 AppendData(array, N);
174 } 223 }
175 224
176 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } 225 void AppendData(const BufferT& buf) { AppendData(buf.data(), buf.size()); }
177 226
178 // Append at most |max_bytes| of data to the end of the buffer, using the 227 // Append at most |max_elements| to the end of the buffer, using the function
179 // function |setter|, which should have the following signature: 228 // |setter|, which should have the following signature:
180 // size_t setter(ArrayView<T> view) 229 // size_t setter(ArrayView<U> view)
181 // |setter| is given an appropriately typed ArrayView of the area in which to 230 // |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 231 // 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 <= 232 // return the number of elements actually written. This number must be <=
184 // |max_bytes|. 233 // |max_elements|.
185 template <typename T = uint8_t, typename F, 234 template <typename U = T,
186 typename internal::ByteType<T>::t = 0> 235 typename F,
187 size_t AppendData(size_t max_bytes, F&& setter) { 236 typename std::enable_if<
237 internal::BufferCompat<T, U>::value>::type* = nullptr>
238 size_t AppendData(size_t max_elements, F&& setter) {
188 RTC_DCHECK(IsConsistent()); 239 RTC_DCHECK(IsConsistent());
189 const size_t old_size = size_; 240 const size_t old_size = size_;
190 SetSize(old_size + max_bytes); 241 SetSize(old_size + max_elements);
191 T *base_ptr = data<T>() + old_size; 242 U* base_ptr = data<U>() + old_size;
192 size_t written_bytes = 243 size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements));
193 setter(rtc::ArrayView<T>(base_ptr, max_bytes));
194 244
195 RTC_CHECK_LE(written_bytes, max_bytes); 245 RTC_CHECK_LE(written_elements, max_elements);
196 size_ = old_size + written_bytes; 246 size_ = old_size + written_elements;
197 RTC_DCHECK(IsConsistent()); 247 RTC_DCHECK(IsConsistent());
198 return written_bytes; 248 return written_elements;
199 } 249 }
200 250
201 // Sets the size of the buffer. If the new size is smaller than the old, the 251 // 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, 252 // 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 253 // the existing contents will be kept and the new space will be
204 // uninitialized. 254 // uninitialized.
205 void SetSize(size_t size) { 255 void SetSize(size_t size) {
206 EnsureCapacity(size); 256 EnsureCapacity(size);
207 size_ = size; 257 size_ = size;
208 } 258 }
209 259
210 // Ensure that the buffer size can be increased to at least capacity without 260 // Ensure that the buffer size can be increased to at least capacity without
211 // further reallocation. (Of course, this operation might need to reallocate 261 // further reallocation. (Of course, this operation might need to reallocate
212 // the buffer.) 262 // the buffer.)
213 void EnsureCapacity(size_t capacity) { 263 void EnsureCapacity(size_t capacity) {
214 RTC_DCHECK(IsConsistent()); 264 RTC_DCHECK(IsConsistent());
215 if (capacity <= capacity_) 265 if (capacity <= capacity_)
216 return; 266 return;
217 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); 267 std::unique_ptr<T[]> new_data(new T[capacity]);
218 std::memcpy(new_data.get(), data_.get(), size_); 268 std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
219 data_ = std::move(new_data); 269 data_ = std::move(new_data);
220 capacity_ = capacity; 270 capacity_ = capacity;
221 RTC_DCHECK(IsConsistent()); 271 RTC_DCHECK(IsConsistent());
222 } 272 }
223 273
224 // Resets the buffer to zero size without altering capacity. Works even if the 274 // Resets the buffer to zero size without altering capacity. Works even if the
225 // buffer has been moved from. 275 // buffer has been moved from.
226 void Clear() { 276 void Clear() {
227 size_ = 0; 277 size_ = 0;
228 RTC_DCHECK(IsConsistent()); 278 RTC_DCHECK(IsConsistent());
229 } 279 }
230 280
231 // Swaps two buffers. Also works for buffers that have been moved from. 281 // Swaps two buffers. Also works for buffers that have been moved from.
232 friend void swap(Buffer& a, Buffer& b) { 282 friend void swap(BufferT& a, BufferT& b) {
233 using std::swap; 283 using std::swap;
234 swap(a.size_, b.size_); 284 swap(a.size_, b.size_);
235 swap(a.capacity_, b.capacity_); 285 swap(a.capacity_, b.capacity_);
236 swap(a.data_, b.data_); 286 swap(a.data_, b.data_);
237 } 287 }
238 288
239 private: 289 private:
240 // Precondition for all methods except Clear and the destructor. 290 // Precondition for all methods except Clear and the destructor.
241 // Postcondition for all methods except move construction and move 291 // Postcondition for all methods except move construction and move
242 // assignment, which leave the moved-from object in a possibly inconsistent 292 // assignment, which leave the moved-from object in a possibly inconsistent
(...skipping 12 matching lines...) Expand all
255 capacity_ = 0; 305 capacity_ = 0;
256 #else 306 #else
257 // Ensure that *this is always inconsistent, to provoke bugs. 307 // Ensure that *this is always inconsistent, to provoke bugs.
258 size_ = 1; 308 size_ = 1;
259 capacity_ = 0; 309 capacity_ = 0;
260 #endif 310 #endif
261 } 311 }
262 312
263 size_t size_; 313 size_t size_;
264 size_t capacity_; 314 size_t capacity_;
265 std::unique_ptr<uint8_t[]> data_; 315 std::unique_ptr<T[]> data_;
316 };
266 317
267 RTC_DISALLOW_COPY_AND_ASSIGN(Buffer); 318 // By far the most common sort of buffer.
268 }; 319 using Buffer = BufferT<uint8_t>;
269 320
270 } // namespace rtc 321 } // namespace rtc
271 322
272 #endif // WEBRTC_BASE_BUFFER_H_ 323 #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