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

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

Powered by Google App Engine
This is Rietveld 408576698