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

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

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

Powered by Google App Engine
This is Rietveld 408576698