OLD | NEW |
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 <algorithm> | |
15 #include <cstring> | |
16 #include <memory> | |
17 #include <type_traits> | |
18 #include <utility> | |
19 | 14 |
20 #include "webrtc/base/array_view.h" | 15 // This header is deprecated and is just left here temporarily during |
21 #include "webrtc/base/checks.h" | 16 // refactoring. See https://bugs.webrtc.org/7634 for more details. |
22 #include "webrtc/base/type_traits.h" | 17 #include "webrtc/rtc_base/buffer.h" |
23 | |
24 namespace rtc { | |
25 | |
26 namespace internal { | |
27 | |
28 // (Internal; please don't use outside this file.) Determines if elements of | |
29 // type U are compatible with a BufferT<T>. For most types, we just ignore | |
30 // top-level const and forbid top-level volatile and require T and U to be | |
31 // otherwise equal, but all byte-sized integers (notably char, int8_t, and | |
32 // uint8_t) are compatible with each other. (Note: We aim to get rid of this | |
33 // behavior, and treat all types the same.) | |
34 template <typename T, typename U> | |
35 struct BufferCompat { | |
36 static constexpr bool value = | |
37 !std::is_volatile<U>::value && | |
38 ((std::is_integral<T>::value && sizeof(T) == 1) | |
39 ? (std::is_integral<U>::value && sizeof(U) == 1) | |
40 : (std::is_same<T, typename std::remove_const<U>::type>::value)); | |
41 }; | |
42 | |
43 } // namespace internal | |
44 | |
45 // Basic buffer class, can be grown and shrunk dynamically. | |
46 // Unlike std::string/vector, does not initialize data when increasing size. | |
47 template <typename T> | |
48 class BufferT { | |
49 // We want T's destructor and default constructor to be trivial, i.e. perform | |
50 // no action, so that we don't have to touch the memory we allocate and | |
51 // deallocate. And we want T to be trivially copyable, so that we can copy T | |
52 // instances with std::memcpy. This is precisely the definition of a trivial | |
53 // type. | |
54 static_assert(std::is_trivial<T>::value, "T must be a trivial type."); | |
55 | |
56 // This class relies heavily on being able to mutate its data. | |
57 static_assert(!std::is_const<T>::value, "T may not be const"); | |
58 | |
59 public: | |
60 // An empty BufferT. | |
61 BufferT() : size_(0), capacity_(0), data_(nullptr) { | |
62 RTC_DCHECK(IsConsistent()); | |
63 } | |
64 | |
65 // Disable copy construction and copy assignment, since copying a buffer is | |
66 // expensive enough that we want to force the user to be explicit about it. | |
67 BufferT(const BufferT&) = delete; | |
68 BufferT& operator=(const BufferT&) = delete; | |
69 | |
70 BufferT(BufferT&& buf) | |
71 : size_(buf.size()), | |
72 capacity_(buf.capacity()), | |
73 data_(std::move(buf.data_)) { | |
74 RTC_DCHECK(IsConsistent()); | |
75 buf.OnMovedFrom(); | |
76 } | |
77 | |
78 // Construct a buffer with the specified number of uninitialized elements. | |
79 explicit BufferT(size_t size) : BufferT(size, size) {} | |
80 | |
81 BufferT(size_t size, size_t capacity) | |
82 : size_(size), | |
83 capacity_(std::max(size, capacity)), | |
84 data_(new T[capacity_]) { | |
85 RTC_DCHECK(IsConsistent()); | |
86 } | |
87 | |
88 // Construct a buffer and copy the specified number of elements into it. | |
89 template <typename U, | |
90 typename std::enable_if< | |
91 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
92 BufferT(const U* data, size_t size) : BufferT(data, size, size) {} | |
93 | |
94 template <typename U, | |
95 typename std::enable_if< | |
96 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
97 BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) { | |
98 static_assert(sizeof(T) == sizeof(U), ""); | |
99 std::memcpy(data_.get(), data, size * sizeof(U)); | |
100 } | |
101 | |
102 // Construct a buffer from the contents of an array. | |
103 template <typename U, | |
104 size_t N, | |
105 typename std::enable_if< | |
106 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
107 BufferT(U (&array)[N]) : BufferT(array, N) {} | |
108 | |
109 // Get a pointer to the data. Just .data() will give you a (const) T*, but if | |
110 // T is a byte-sized integer, you may also use .data<U>() for any other | |
111 // byte-sized integer U. | |
112 template <typename U = T, | |
113 typename std::enable_if< | |
114 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
115 const U* data() const { | |
116 RTC_DCHECK(IsConsistent()); | |
117 return reinterpret_cast<U*>(data_.get()); | |
118 } | |
119 | |
120 template <typename U = T, | |
121 typename std::enable_if< | |
122 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
123 U* data() { | |
124 RTC_DCHECK(IsConsistent()); | |
125 return reinterpret_cast<U*>(data_.get()); | |
126 } | |
127 | |
128 bool empty() const { | |
129 RTC_DCHECK(IsConsistent()); | |
130 return size_ == 0; | |
131 } | |
132 | |
133 size_t size() const { | |
134 RTC_DCHECK(IsConsistent()); | |
135 return size_; | |
136 } | |
137 | |
138 size_t capacity() const { | |
139 RTC_DCHECK(IsConsistent()); | |
140 return capacity_; | |
141 } | |
142 | |
143 BufferT& operator=(BufferT&& buf) { | |
144 RTC_DCHECK(IsConsistent()); | |
145 RTC_DCHECK(buf.IsConsistent()); | |
146 size_ = buf.size_; | |
147 capacity_ = buf.capacity_; | |
148 data_ = std::move(buf.data_); | |
149 buf.OnMovedFrom(); | |
150 return *this; | |
151 } | |
152 | |
153 bool operator==(const BufferT& buf) const { | |
154 RTC_DCHECK(IsConsistent()); | |
155 if (size_ != buf.size_) { | |
156 return false; | |
157 } | |
158 if (std::is_integral<T>::value) { | |
159 // Optimization. | |
160 return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0; | |
161 } | |
162 for (size_t i = 0; i < size_; ++i) { | |
163 if (data_[i] != buf.data_[i]) { | |
164 return false; | |
165 } | |
166 } | |
167 return true; | |
168 } | |
169 | |
170 bool operator!=(const BufferT& buf) const { return !(*this == buf); } | |
171 | |
172 T& operator[](size_t index) { | |
173 RTC_DCHECK_LT(index, size_); | |
174 return data()[index]; | |
175 } | |
176 | |
177 T operator[](size_t index) const { | |
178 RTC_DCHECK_LT(index, size_); | |
179 return data()[index]; | |
180 } | |
181 | |
182 T* begin() { return data(); } | |
183 T* end() { return data() + size(); } | |
184 const T* begin() const { return data(); } | |
185 const T* end() const { return data() + size(); } | |
186 const T* cbegin() const { return data(); } | |
187 const T* cend() const { return data() + size(); } | |
188 | |
189 // The SetData functions replace the contents of the buffer. They accept the | |
190 // same input types as the constructors. | |
191 template <typename U, | |
192 typename std::enable_if< | |
193 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
194 void SetData(const U* data, size_t size) { | |
195 RTC_DCHECK(IsConsistent()); | |
196 size_ = 0; | |
197 AppendData(data, size); | |
198 } | |
199 | |
200 template <typename U, | |
201 size_t N, | |
202 typename std::enable_if< | |
203 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
204 void SetData(const U (&array)[N]) { | |
205 SetData(array, N); | |
206 } | |
207 | |
208 template <typename W, | |
209 typename std::enable_if< | |
210 HasDataAndSize<const W, const T>::value>::type* = nullptr> | |
211 void SetData(const W& w) { | |
212 SetData(w.data(), w.size()); | |
213 } | |
214 | |
215 // Replace the data in the buffer with at most |max_elements| of data, using | |
216 // the function |setter|, which should have the following signature: | |
217 // size_t setter(ArrayView<U> view) | |
218 // |setter| is given an appropriately typed ArrayView of the area in which to | |
219 // write the data (i.e. starting at the beginning of the buffer) and should | |
220 // return the number of elements actually written. This number must be <= | |
221 // |max_elements|. | |
222 template <typename U = T, | |
223 typename F, | |
224 typename std::enable_if< | |
225 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
226 size_t SetData(size_t max_elements, F&& setter) { | |
227 RTC_DCHECK(IsConsistent()); | |
228 size_ = 0; | |
229 return AppendData<U>(max_elements, std::forward<F>(setter)); | |
230 } | |
231 | |
232 // The AppendData functions add data to the end of the buffer. They accept | |
233 // the same input types as the constructors. | |
234 template <typename U, | |
235 typename std::enable_if< | |
236 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
237 void AppendData(const U* data, size_t size) { | |
238 RTC_DCHECK(IsConsistent()); | |
239 const size_t new_size = size_ + size; | |
240 EnsureCapacityWithHeadroom(new_size, true); | |
241 static_assert(sizeof(T) == sizeof(U), ""); | |
242 std::memcpy(data_.get() + size_, data, size * sizeof(U)); | |
243 size_ = new_size; | |
244 RTC_DCHECK(IsConsistent()); | |
245 } | |
246 | |
247 template <typename U, | |
248 size_t N, | |
249 typename std::enable_if< | |
250 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
251 void AppendData(const U (&array)[N]) { | |
252 AppendData(array, N); | |
253 } | |
254 | |
255 template <typename W, | |
256 typename std::enable_if< | |
257 HasDataAndSize<const W, const T>::value>::type* = nullptr> | |
258 void AppendData(const W& w) { | |
259 AppendData(w.data(), w.size()); | |
260 } | |
261 | |
262 template <typename U, | |
263 typename std::enable_if< | |
264 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
265 void AppendData(const U& item) { | |
266 AppendData(&item, 1); | |
267 } | |
268 | |
269 // Append at most |max_elements| to the end of the buffer, using the function | |
270 // |setter|, which should have the following signature: | |
271 // size_t setter(ArrayView<U> view) | |
272 // |setter| is given an appropriately typed ArrayView of the area in which to | |
273 // write the data (i.e. starting at the former end of the buffer) and should | |
274 // return the number of elements actually written. This number must be <= | |
275 // |max_elements|. | |
276 template <typename U = T, | |
277 typename F, | |
278 typename std::enable_if< | |
279 internal::BufferCompat<T, U>::value>::type* = nullptr> | |
280 size_t AppendData(size_t max_elements, F&& setter) { | |
281 RTC_DCHECK(IsConsistent()); | |
282 const size_t old_size = size_; | |
283 SetSize(old_size + max_elements); | |
284 U* base_ptr = data<U>() + old_size; | |
285 size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements)); | |
286 | |
287 RTC_CHECK_LE(written_elements, max_elements); | |
288 size_ = old_size + written_elements; | |
289 RTC_DCHECK(IsConsistent()); | |
290 return written_elements; | |
291 } | |
292 | |
293 // Sets the size of the buffer. If the new size is smaller than the old, the | |
294 // buffer contents will be kept but truncated; if the new size is greater, | |
295 // the existing contents will be kept and the new space will be | |
296 // uninitialized. | |
297 void SetSize(size_t size) { | |
298 EnsureCapacityWithHeadroom(size, true); | |
299 size_ = size; | |
300 } | |
301 | |
302 // Ensure that the buffer size can be increased to at least capacity without | |
303 // further reallocation. (Of course, this operation might need to reallocate | |
304 // the buffer.) | |
305 void EnsureCapacity(size_t capacity) { | |
306 // Don't allocate extra headroom, since the user is asking for a specific | |
307 // capacity. | |
308 EnsureCapacityWithHeadroom(capacity, false); | |
309 } | |
310 | |
311 // Resets the buffer to zero size without altering capacity. Works even if the | |
312 // buffer has been moved from. | |
313 void Clear() { | |
314 size_ = 0; | |
315 RTC_DCHECK(IsConsistent()); | |
316 } | |
317 | |
318 // Swaps two buffers. Also works for buffers that have been moved from. | |
319 friend void swap(BufferT& a, BufferT& b) { | |
320 using std::swap; | |
321 swap(a.size_, b.size_); | |
322 swap(a.capacity_, b.capacity_); | |
323 swap(a.data_, b.data_); | |
324 } | |
325 | |
326 private: | |
327 void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) { | |
328 RTC_DCHECK(IsConsistent()); | |
329 if (capacity <= capacity_) | |
330 return; | |
331 | |
332 // If the caller asks for extra headroom, ensure that the new capacity is | |
333 // >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent | |
334 // quadratic behavior; as to why we pick 1.5 in particular, see | |
335 // https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and | |
336 // http://www.gahcep.com/cpp-internals-stl-vector-part-1/. | |
337 const size_t new_capacity = | |
338 extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2) | |
339 : capacity; | |
340 | |
341 std::unique_ptr<T[]> new_data(new T[new_capacity]); | |
342 std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); | |
343 data_ = std::move(new_data); | |
344 capacity_ = new_capacity; | |
345 RTC_DCHECK(IsConsistent()); | |
346 } | |
347 | |
348 // Precondition for all methods except Clear and the destructor. | |
349 // Postcondition for all methods except move construction and move | |
350 // assignment, which leave the moved-from object in a possibly inconsistent | |
351 // state. | |
352 bool IsConsistent() const { | |
353 return (data_ || capacity_ == 0) && capacity_ >= size_; | |
354 } | |
355 | |
356 // Called when *this has been moved from. Conceptually it's a no-op, but we | |
357 // can mutate the state slightly to help subsequent sanity checks catch bugs. | |
358 void OnMovedFrom() { | |
359 #if RTC_DCHECK_IS_ON | |
360 // Make *this consistent and empty. Shouldn't be necessary, but better safe | |
361 // than sorry. | |
362 size_ = 0; | |
363 capacity_ = 0; | |
364 #else | |
365 // Ensure that *this is always inconsistent, to provoke bugs. | |
366 size_ = 1; | |
367 capacity_ = 0; | |
368 #endif | |
369 } | |
370 | |
371 size_t size_; | |
372 size_t capacity_; | |
373 std::unique_ptr<T[]> data_; | |
374 }; | |
375 | |
376 // By far the most common sort of buffer. | |
377 using Buffer = BufferT<uint8_t>; | |
378 | |
379 } // namespace rtc | |
380 | 18 |
381 #endif // WEBRTC_BASE_BUFFER_H_ | 19 #endif // WEBRTC_BASE_BUFFER_H_ |
OLD | NEW |