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> // std::swap (pre-C++11) | 14 #include <algorithm> // std::swap (pre-C++11) |
15 #include <cassert> | |
16 #include <cstring> | 15 #include <cstring> |
17 #include <memory> | 16 #include <memory> |
18 #include <utility> // std::swap (C++11 and later) | 17 #include <utility> // std::swap (C++11 and later) |
19 | 18 |
19 #include "webrtc/base/checks.h" | |
20 #include "webrtc/base/constructormagic.h" | 20 #include "webrtc/base/constructormagic.h" |
21 #include "webrtc/base/deprecation.h" | 21 #include "webrtc/base/deprecation.h" |
22 #include "webrtc/base/refcount.h" | |
23 #include "webrtc/base/scoped_ref_ptr.h" | |
22 | 24 |
23 namespace rtc { | 25 namespace rtc { |
24 | 26 |
27 class Buffer; | |
28 | |
25 namespace internal { | 29 namespace internal { |
26 | 30 |
27 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T | 31 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T |
28 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like | 32 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like |
29 // this: | 33 // this: |
30 // | 34 // |
31 // template <typename T, typename ByteType<T>::t = 0> | 35 // template <typename T, typename ByteType<T>::t = 0> |
32 // void foo(T* x); | 36 // void foo(T* x); |
33 // | 37 // |
34 // to let foo<T> be defined only for byte-sized integers. | 38 // to let foo<T> be defined only for byte-sized integers. |
35 template <typename T> | 39 template <typename T> |
36 struct ByteType { | 40 struct ByteType { |
37 private: | 41 private: |
38 static int F(uint8_t*); | 42 static int F(uint8_t*); |
39 static int F(int8_t*); | 43 static int F(int8_t*); |
40 static int F(char*); | 44 static int F(char*); |
41 | 45 |
42 public: | 46 public: |
43 using t = decltype(F(static_cast<T*>(nullptr))); | 47 using t = decltype(F(static_cast<T*>(nullptr))); |
44 }; | 48 }; |
45 | 49 |
50 // Data stored in a buffer. RefCounted and will be shared between buffers that | |
51 // are cloned to avoid unnecessary allocations / copies. | |
52 class BufferData : public RefCountedObject<RefCountInterface> { | |
53 public: | |
54 BufferData(); | |
55 | |
56 explicit BufferData(const BufferData& data); | |
57 | |
58 // Construct data with the specified number of uninitialized bytes. | |
59 explicit BufferData(size_t size); | |
60 BufferData(size_t size, size_t capacity); | |
61 | |
62 // Construct data and copy the specified number of bytes into it. | |
63 BufferData(const void* data, size_t size); | |
64 BufferData(const void* data, size_t size, size_t capacity); | |
kwiberg-webrtc
2016/02/14 14:43:46
Buffer itself has a large number of constructors b
joachim
2016/02/14 17:10:16
Done.
| |
65 | |
66 size_t size() const { | |
67 RTC_DCHECK(IsConsistent()); | |
68 return size_; | |
69 } | |
70 size_t capacity() const { | |
71 RTC_DCHECK(IsConsistent()); | |
72 return capacity_; | |
73 } | |
74 uint8_t* data() const { | |
75 RTC_DCHECK(IsConsistent()); | |
76 return data_.get(); | |
77 } | |
kwiberg-webrtc
2016/02/14 14:43:46
Why have all these accessors when you allow Buffer
joachim
2016/02/14 17:10:16
Right, the "IsConsistent" checks are already done
| |
78 | |
79 void SetData(const void* data, size_t size) { | |
80 RTC_DCHECK(IsConsistent()); | |
81 size_ = 0; | |
82 AppendData(data, size); | |
83 } | |
84 | |
85 void AppendData(const void* data, size_t size) { | |
86 RTC_DCHECK(IsConsistent()); | |
87 const size_t new_size = size_ + size; | |
88 EnsureCapacity(new_size); | |
89 std::memcpy(data_.get() + size_, data, size); | |
90 size_ = new_size; | |
91 RTC_DCHECK(IsConsistent()); | |
92 } | |
93 | |
94 // Sets the size of the data. If the new size is smaller than the old, the | |
95 // data contents will be kept but truncated; if the new size is greater, | |
96 // the existing contents will be kept and the new space will be | |
97 // uninitialized. | |
98 void SetSize(size_t size) { | |
99 EnsureCapacity(size); | |
100 size_ = size; | |
101 } | |
102 | |
103 // Ensure that the data size can be increased to at least capacity without | |
104 // further reallocation. (Of course, this operation might need to reallocate | |
105 // the buffer.) | |
106 void EnsureCapacity(size_t capacity) { | |
107 RTC_DCHECK(IsConsistent()); | |
108 if (capacity <= capacity_) { | |
109 return; | |
110 } | |
111 | |
112 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); | |
113 std::memcpy(new_data.get(), data_.get(), size_); | |
114 data_ = std::move(new_data); | |
115 capacity_ = capacity; | |
116 RTC_DCHECK(IsConsistent()); | |
117 } | |
118 | |
119 // Resets the data to zero size and capacity. | |
120 void Clear() { | |
121 data_.reset(); | |
122 size_ = 0; | |
123 capacity_ = 0; | |
124 RTC_DCHECK(IsConsistent()); | |
125 } | |
126 | |
127 private: | |
128 friend class rtc::Buffer; | |
kwiberg-webrtc
2016/02/14 14:43:45
Hmm. Since BufferData is intended to be used only
joachim
2016/02/14 17:10:16
Ok, makes sense.
| |
129 | |
130 // Precondition for all methods except Clear and the destructor. | |
131 // Postcondition for all methods except move construction and move | |
132 // assignment, which leave the moved-from object in a possibly inconsistent | |
133 // state. | |
134 bool IsConsistent() const { | |
135 return (data_ || capacity_ == 0) && capacity_ >= size_; | |
136 } | |
137 | |
138 #ifndef NDEBUG | |
139 // Called when *this has been moved from. Conceptually it's a no-op, but we | |
140 // can mutate the state slightly to help subsequent sanity checks catch bugs. | |
141 void OnMovedFrom() { | |
142 // Ensure that *this is always inconsistent, to provoke bugs. | |
143 size_ = 1; | |
144 capacity_ = 0; | |
145 } | |
146 #endif | |
147 | |
148 size_t size_; | |
149 size_t capacity_; | |
150 std::unique_ptr<uint8_t[]> data_; | |
151 }; | |
152 | |
46 } // namespace internal | 153 } // namespace internal |
47 | 154 |
48 // Basic buffer class, can be grown and shrunk dynamically. | 155 // Basic buffer class, can be grown and shrunk dynamically. |
49 // Unlike std::string/vector, does not initialize data when expanding capacity. | 156 // Unlike std::string/vector, does not initialize data when expanding capacity. |
157 // The underlying memory is shared between copies of buffers and cloned if a | |
158 // shared buffer is being modified. | |
50 class Buffer { | 159 class Buffer { |
51 public: | 160 public: |
52 Buffer(); // An empty buffer. | 161 Buffer(); // An empty buffer. |
53 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer. | 162 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer. |
54 Buffer(Buffer&& buf); // Move contents from an existing buffer. | 163 Buffer(Buffer&& buf); // Move contents from an existing buffer. |
55 | 164 |
56 // Construct a buffer with the specified number of uninitialized bytes. | 165 // Construct a buffer with the specified number of uninitialized bytes. |
57 explicit Buffer(size_t size); | 166 explicit Buffer(size_t size); |
58 Buffer(size_t size, size_t capacity); | 167 Buffer(size_t size, size_t capacity); |
59 | 168 |
60 // Construct a buffer and copy the specified number of bytes into it. The | 169 // Construct a buffer and copy the specified number of bytes into it. The |
61 // source array may be (const) uint8_t*, int8_t*, or char*. | 170 // source array may be (const) uint8_t*, int8_t*, or char*. |
62 template <typename T, typename internal::ByteType<T>::t = 0> | 171 template <typename T, typename internal::ByteType<T>::t = 0> |
63 Buffer(const T* data, size_t size) | 172 Buffer(const T* data, size_t size) |
64 : Buffer(data, size, size) {} | 173 : Buffer(data, size, size) {} |
65 template <typename T, typename internal::ByteType<T>::t = 0> | 174 template <typename T, typename internal::ByteType<T>::t = 0> |
66 Buffer(const T* data, size_t size, size_t capacity) | 175 Buffer(const T* data, size_t size, size_t capacity) |
67 : Buffer(size, capacity) { | 176 : Buffer(size, capacity) { |
68 std::memcpy(data_.get(), data, size); | 177 std::memcpy(data_->data(), data, size); |
69 } | 178 } |
70 | 179 |
71 // Construct a buffer from the contents of an array. | 180 // Construct a buffer from the contents of an array. |
72 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 181 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
73 Buffer(const T(&array)[N]) | 182 Buffer(const T(&array)[N]) // NOLINT: runtime/explicit |
74 : Buffer(array, N) {} | 183 : Buffer(array, N) {} |
75 | 184 |
76 ~Buffer(); | 185 ~Buffer(); |
77 | 186 |
78 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, | 187 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, |
79 // but you may also use .data<int8_t>() and .data<char>(). | 188 // but you may also use .data<int8_t>() and .data<char>(). |
80 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> | 189 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
81 const T* data() const { | 190 const T* data() const { |
82 assert(IsConsistent()); | 191 RTC_DCHECK(IsConsistent()); |
83 return reinterpret_cast<T*>(data_.get()); | 192 return reinterpret_cast<T*>(data_->data()); |
84 } | 193 } |
194 // Get writable pointer to the data. This will create a copy of the underlying | |
195 // data if it is shared with other buffers. | |
85 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> | 196 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
86 T* data() { | 197 T* data() { |
87 assert(IsConsistent()); | 198 CloneDataIfReferenced(); |
88 return reinterpret_cast<T*>(data_.get()); | 199 return reinterpret_cast<T*>(data_->data()); |
89 } | 200 } |
90 | 201 |
91 size_t size() const { | 202 size_t size() const { |
92 assert(IsConsistent()); | 203 RTC_DCHECK(IsConsistent()); |
93 return size_; | 204 return data_->size(); |
94 } | 205 } |
95 size_t capacity() const { | 206 size_t capacity() const { |
96 assert(IsConsistent()); | 207 RTC_DCHECK(IsConsistent()); |
97 return capacity_; | 208 return data_->capacity(); |
98 } | 209 } |
99 | 210 |
100 Buffer& operator=(const Buffer& buf) { | 211 Buffer& operator=(const Buffer& buf) { |
101 if (&buf != this) | 212 if (&buf != this) { |
102 SetData(buf.data(), buf.size()); | 213 SetData(buf); |
214 } | |
103 return *this; | 215 return *this; |
104 } | 216 } |
105 Buffer& operator=(Buffer&& buf) { | 217 Buffer& operator=(Buffer&& buf) { |
106 assert(IsConsistent()); | 218 if (&buf != this) { |
107 assert(buf.IsConsistent()); | 219 RTC_DCHECK(IsConsistent()); |
108 size_ = buf.size_; | 220 RTC_DCHECK(buf.IsConsistent()); |
109 capacity_ = buf.capacity_; | 221 data_ = std::move(buf.data_); |
110 data_ = std::move(buf.data_); | 222 buf.OnMovedFrom(); |
111 buf.OnMovedFrom(); | 223 } |
112 return *this; | 224 return *this; |
113 } | 225 } |
114 | 226 |
115 bool operator==(const Buffer& buf) const { | 227 bool operator==(const Buffer& buf) const { |
116 assert(IsConsistent()); | 228 return data_ == buf.data_ || |
117 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0; | 229 (size() == buf.size() && memcmp(data(), buf.data(), size()) == 0); |
118 } | 230 } |
119 | 231 |
120 bool operator!=(const Buffer& buf) const { return !(*this == buf); } | 232 bool operator!=(const Buffer& buf) const { return !(*this == buf); } |
121 | 233 |
122 // Replace the contents of the buffer. Accepts the same types as the | 234 // Replace the contents of the buffer. Accepts the same types as the |
123 // constructors. | 235 // constructors. |
124 template <typename T, typename internal::ByteType<T>::t = 0> | 236 template <typename T, typename internal::ByteType<T>::t = 0> |
125 void SetData(const T* data, size_t size) { | 237 void SetData(const T* data, size_t size) { |
126 assert(IsConsistent()); | 238 RTC_DCHECK(IsConsistent()); |
127 size_ = 0; | 239 if (!data_->HasOneRef()) { |
128 AppendData(data, size); | 240 data_ = new internal::BufferData(data, size); |
241 } else { | |
242 data_->SetData(data, size); | |
243 } | |
129 } | 244 } |
130 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 245 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
131 void SetData(const T(&array)[N]) { | 246 void SetData(const T(&array)[N]) { |
132 SetData(array, N); | 247 SetData(array, N); |
133 } | 248 } |
134 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); } | 249 void SetData(const Buffer& buf) { |
250 if (&buf != this) { | |
251 RTC_DCHECK(IsConsistent()); | |
252 RTC_DCHECK(buf.IsConsistent()); | |
253 data_ = buf.data_; | |
254 } | |
255 } | |
135 | 256 |
136 // Append data to the buffer. Accepts the same types as the constructors. | 257 // Append data to the buffer. Accepts the same types as the constructors. |
137 template <typename T, typename internal::ByteType<T>::t = 0> | 258 template <typename T, typename internal::ByteType<T>::t = 0> |
138 void AppendData(const T* data, size_t size) { | 259 void AppendData(const T* data, size_t size) { |
139 assert(IsConsistent()); | 260 CloneDataIfReferenced(); |
140 const size_t new_size = size_ + size; | 261 data_->AppendData(data, size); |
141 EnsureCapacity(new_size); | |
142 std::memcpy(data_.get() + size_, data, size); | |
143 size_ = new_size; | |
144 assert(IsConsistent()); | |
145 } | 262 } |
146 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 263 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
147 void AppendData(const T(&array)[N]) { | 264 void AppendData(const T(&array)[N]) { |
148 AppendData(array, N); | 265 AppendData(array, N); |
149 } | 266 } |
150 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } | 267 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } |
151 | 268 |
152 // Sets the size of the buffer. If the new size is smaller than the old, the | 269 // Sets the size of the buffer. If the new size is smaller than the old, the |
153 // buffer contents will be kept but truncated; if the new size is greater, | 270 // buffer contents will be kept but truncated; if the new size is greater, |
154 // the existing contents will be kept and the new space will be | 271 // the existing contents will be kept and the new space will be |
155 // uninitialized. | 272 // uninitialized. |
156 void SetSize(size_t size) { | 273 void SetSize(size_t size) { |
157 EnsureCapacity(size); | 274 CloneDataIfReferenced(); |
158 size_ = size; | 275 data_->SetSize(size); |
159 } | 276 } |
160 | 277 |
161 // Ensure that the buffer size can be increased to at least capacity without | 278 // Ensure that the buffer size can be increased to at least capacity without |
162 // further reallocation. (Of course, this operation might need to reallocate | 279 // further reallocation. (Of course, this operation might need to reallocate |
163 // the buffer.) | 280 // the buffer.) |
164 void EnsureCapacity(size_t capacity) { | 281 void EnsureCapacity(size_t capacity) { |
165 assert(IsConsistent()); | 282 RTC_DCHECK(IsConsistent()); |
166 if (capacity <= capacity_) | 283 if (capacity <= data_->capacity()) |
167 return; | 284 return; |
168 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); | 285 |
169 std::memcpy(new_data.get(), data_.get(), size_); | 286 CloneDataIfReferenced(); |
170 data_ = std::move(new_data); | 287 data_->EnsureCapacity(capacity); |
171 capacity_ = capacity; | |
172 assert(IsConsistent()); | |
173 } | 288 } |
174 | 289 |
175 // b.Pass() does the same thing as std::move(b). | 290 // b.Pass() does the same thing as std::move(b). |
176 // Deprecated; remove in March 2016 (bug 5373). | 291 // Deprecated; remove in March 2016 (bug 5373). |
177 RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); } | 292 RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); } |
178 Buffer&& DEPRECATED_Pass() { | 293 Buffer&& DEPRECATED_Pass() { |
179 assert(IsConsistent()); | 294 RTC_DCHECK(IsConsistent()); |
180 return std::move(*this); | 295 return std::move(*this); |
181 } | 296 } |
182 | 297 |
183 // Resets the buffer to zero size and capacity. Works even if the buffer has | 298 // Resets the buffer to zero size and capacity. |
184 // been moved from. | |
185 void Clear() { | 299 void Clear() { |
186 data_.reset(); | 300 if (!data_->HasOneRef()) { |
187 size_ = 0; | 301 data_ = new internal::BufferData(); |
188 capacity_ = 0; | 302 } else { |
189 assert(IsConsistent()); | 303 data_->Clear(); |
304 } | |
305 RTC_DCHECK(IsConsistent()); | |
190 } | 306 } |
191 | 307 |
192 // Swaps two buffers. Also works for buffers that have been moved from. | 308 // Swaps two buffers. |
193 friend void swap(Buffer& a, Buffer& b) { | 309 friend void swap(Buffer& a, Buffer& b) { |
194 using std::swap; | 310 std::swap(a.data_, b.data_); |
195 swap(a.size_, b.size_); | |
196 swap(a.capacity_, b.capacity_); | |
197 swap(a.data_, b.data_); | |
198 } | 311 } |
199 | 312 |
200 private: | 313 private: |
201 // Precondition for all methods except Clear and the destructor. | 314 // Precondition for all methods except Clear and the destructor. |
202 // Postcondition for all methods except move construction and move | 315 // Postcondition for all methods except move construction and move |
203 // assignment, which leave the moved-from object in a possibly inconsistent | 316 // assignment, which leave the moved-from object in a possibly inconsistent |
204 // state. | 317 // state. |
205 bool IsConsistent() const { | 318 bool IsConsistent() const { |
206 return (data_ || capacity_ == 0) && capacity_ >= size_; | 319 return data_ != nullptr && data_->IsConsistent(); |
207 } | 320 } |
208 | 321 |
209 // Called when *this has been moved from. Conceptually it's a no-op, but we | 322 // Called when *this has been moved from. Conceptually it's a no-op, but we |
210 // can mutate the state slightly to help subsequent sanity checks catch bugs. | 323 // can mutate the state slightly to help subsequent sanity checks catch bugs. |
211 void OnMovedFrom() { | 324 void OnMovedFrom() { |
212 #ifdef NDEBUG | 325 data_ = new internal::BufferData(); |
213 // Make *this consistent and empty. Shouldn't be necessary, but better safe | 326 #ifndef NDEBUG |
214 // than sorry. | |
215 size_ = 0; | |
216 capacity_ = 0; | |
217 #else | |
218 // Ensure that *this is always inconsistent, to provoke bugs. | 327 // Ensure that *this is always inconsistent, to provoke bugs. |
219 size_ = 1; | 328 data_->OnMovedFrom(); |
220 capacity_ = 0; | |
221 #endif | 329 #endif |
222 } | 330 } |
223 | 331 |
224 size_t size_; | 332 // Create a copy of the underlying data if it is referenced from other Buffer |
225 size_t capacity_; | 333 // objects. |
226 std::unique_ptr<uint8_t[]> data_; | 334 void CloneDataIfReferenced() { |
335 RTC_DCHECK(IsConsistent()); | |
336 if (data_->HasOneRef()) { | |
337 return; | |
338 } | |
339 | |
340 data_ = new internal::BufferData(*data_); | |
341 } | |
342 | |
343 scoped_refptr<internal::BufferData> data_; | |
227 }; | 344 }; |
228 | 345 |
229 } // namespace rtc | 346 } // namespace rtc |
230 | 347 |
231 #endif // WEBRTC_BASE_BUFFER_H_ | 348 #endif // WEBRTC_BASE_BUFFER_H_ |
OLD | NEW |