blob: 16e15f946b186e71f460fe36c3ab2a21597a11b7 [file] [log] [blame]
jbauch13041cf2016-02-25 06:16:52 -08001/*
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_COPYONWRITEBUFFER_H_
12#define WEBRTC_BASE_COPYONWRITEBUFFER_H_
13
14#include <algorithm>
15#include <utility>
16
17#include "webrtc/base/buffer.h"
18#include "webrtc/base/checks.h"
19#include "webrtc/base/refcount.h"
20#include "webrtc/base/scoped_ref_ptr.h"
21
22namespace rtc {
23
24class CopyOnWriteBuffer {
25 public:
26 // An empty buffer.
27 CopyOnWriteBuffer();
28 // Copy size and contents of an existing buffer.
29 CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
30 // Move contents from an existing buffer.
31 CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
32
33 // Construct a buffer with the specified number of uninitialized bytes.
34 explicit CopyOnWriteBuffer(size_t size);
35 CopyOnWriteBuffer(size_t size, size_t capacity);
36
37 // Construct a buffer and copy the specified number of bytes into it. The
38 // source array may be (const) uint8_t*, int8_t*, or char*.
kwiberga4ac4782016-04-29 08:00:22 -070039 template <typename T,
40 typename std::enable_if<
41 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -080042 CopyOnWriteBuffer(const T* data, size_t size)
43 : CopyOnWriteBuffer(data, size, size) {}
kwiberga4ac4782016-04-29 08:00:22 -070044 template <typename T,
45 typename std::enable_if<
46 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -080047 CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
48 : CopyOnWriteBuffer(size, capacity) {
jbaucheec21bd2016-03-20 06:15:43 -070049 if (buffer_) {
50 std::memcpy(buffer_->data(), data, size);
51 }
jbauch13041cf2016-02-25 06:16:52 -080052 }
53
54 // Construct a buffer from the contents of an array.
kwiberga4ac4782016-04-29 08:00:22 -070055 template <typename T,
56 size_t N,
57 typename std::enable_if<
58 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
59 CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
jbauch13041cf2016-02-25 06:16:52 -080060 : CopyOnWriteBuffer(array, N) {}
61
62 ~CopyOnWriteBuffer();
63
64 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
65 // but you may also use .data<int8_t>() and .data<char>().
kwiberga4ac4782016-04-29 08:00:22 -070066 template <typename T = uint8_t,
67 typename std::enable_if<
68 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -080069 const T* data() const {
70 return cdata<T>();
71 }
72
73 // Get writable pointer to the data. This will create a copy of the underlying
74 // data if it is shared with other buffers.
kwiberga4ac4782016-04-29 08:00:22 -070075 template <typename T = uint8_t,
76 typename std::enable_if<
77 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -080078 T* data() {
79 RTC_DCHECK(IsConsistent());
80 if (!buffer_) {
81 return nullptr;
82 }
83 CloneDataIfReferenced(buffer_->capacity());
84 return buffer_->data<T>();
85 }
86
87 // Get const pointer to the data. This will not create a copy of the
88 // underlying data if it is shared with other buffers.
kwiberga4ac4782016-04-29 08:00:22 -070089 template <typename T = uint8_t,
90 typename std::enable_if<
91 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -080092 T* cdata() const {
93 RTC_DCHECK(IsConsistent());
94 if (!buffer_) {
95 return nullptr;
96 }
97 return buffer_->data<T>();
98 }
99
100 size_t size() const {
101 RTC_DCHECK(IsConsistent());
102 return buffer_ ? buffer_->size() : 0;
103 }
104
105 size_t capacity() const {
106 RTC_DCHECK(IsConsistent());
107 return buffer_ ? buffer_->capacity() : 0;
108 }
109
110 CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
111 RTC_DCHECK(IsConsistent());
112 RTC_DCHECK(buf.IsConsistent());
113 if (&buf != this) {
114 buffer_ = buf.buffer_;
115 }
116 return *this;
117 }
118
119 CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
120 RTC_DCHECK(IsConsistent());
121 RTC_DCHECK(buf.IsConsistent());
danilchapd8a9c532016-07-30 12:39:26 -0700122 buffer_ = std::move(buf.buffer_);
jbauch13041cf2016-02-25 06:16:52 -0800123 return *this;
124 }
125
126 bool operator==(const CopyOnWriteBuffer& buf) const {
127 // Must either use the same buffer internally or have the same contents.
128 RTC_DCHECK(IsConsistent());
129 RTC_DCHECK(buf.IsConsistent());
130 return buffer_.get() == buf.buffer_.get() ||
131 (buffer_.get() && buf.buffer_.get() &&
132 *buffer_.get() == *buf.buffer_.get());
133 }
134
135 bool operator!=(const CopyOnWriteBuffer& buf) const {
136 return !(*this == buf);
137 }
138
jbaucheec21bd2016-03-20 06:15:43 -0700139 uint8_t& operator[](size_t index) {
140 RTC_DCHECK_LT(index, size());
141 return data()[index];
142 }
143
144 uint8_t operator[](size_t index) const {
145 RTC_DCHECK_LT(index, size());
146 return cdata()[index];
147 }
148
jbauch13041cf2016-02-25 06:16:52 -0800149 // Replace the contents of the buffer. Accepts the same types as the
150 // constructors.
kwiberga4ac4782016-04-29 08:00:22 -0700151 template <typename T,
152 typename std::enable_if<
153 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -0800154 void SetData(const T* data, size_t size) {
155 RTC_DCHECK(IsConsistent());
156 if (!buffer_ || !buffer_->HasOneRef()) {
jbaucheec21bd2016-03-20 06:15:43 -0700157 buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size)
158 : nullptr;
jbauch13041cf2016-02-25 06:16:52 -0800159 } else {
160 buffer_->SetData(data, size);
161 }
162 RTC_DCHECK(IsConsistent());
163 }
164
kwiberga4ac4782016-04-29 08:00:22 -0700165 template <typename T,
166 size_t N,
167 typename std::enable_if<
168 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
169 void SetData(const T (&array)[N]) {
jbauch13041cf2016-02-25 06:16:52 -0800170 SetData(array, N);
171 }
172
173 void SetData(const CopyOnWriteBuffer& buf) {
174 RTC_DCHECK(IsConsistent());
175 RTC_DCHECK(buf.IsConsistent());
176 if (&buf != this) {
177 buffer_ = buf.buffer_;
178 }
179 }
180
181 // Append data to the buffer. Accepts the same types as the constructors.
kwiberga4ac4782016-04-29 08:00:22 -0700182 template <typename T,
183 typename std::enable_if<
184 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
jbauch13041cf2016-02-25 06:16:52 -0800185 void AppendData(const T* data, size_t size) {
186 RTC_DCHECK(IsConsistent());
187 if (!buffer_) {
188 buffer_ = new RefCountedObject<Buffer>(data, size);
189 RTC_DCHECK(IsConsistent());
190 return;
191 }
192
193 CloneDataIfReferenced(std::max(buffer_->capacity(),
194 buffer_->size() + size));
195 buffer_->AppendData(data, size);
196 RTC_DCHECK(IsConsistent());
197 }
198
kwiberga4ac4782016-04-29 08:00:22 -0700199 template <typename T,
200 size_t N,
201 typename std::enable_if<
202 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
203 void AppendData(const T (&array)[N]) {
jbauch13041cf2016-02-25 06:16:52 -0800204 AppendData(array, N);
205 }
206
207 void AppendData(const CopyOnWriteBuffer& buf) {
208 AppendData(buf.data(), buf.size());
209 }
210
211 // Sets the size of the buffer. If the new size is smaller than the old, the
212 // buffer contents will be kept but truncated; if the new size is greater,
213 // the existing contents will be kept and the new space will be
214 // uninitialized.
215 void SetSize(size_t size) {
216 RTC_DCHECK(IsConsistent());
217 if (!buffer_) {
218 if (size > 0) {
219 buffer_ = new RefCountedObject<Buffer>(size);
220 }
221 RTC_DCHECK(IsConsistent());
222 return;
223 }
224
225 CloneDataIfReferenced(std::max(buffer_->capacity(), size));
226 buffer_->SetSize(size);
227 RTC_DCHECK(IsConsistent());
228 }
229
230 // Ensure that the buffer size can be increased to at least capacity without
231 // further reallocation. (Of course, this operation might need to reallocate
232 // the buffer.)
233 void EnsureCapacity(size_t capacity) {
234 RTC_DCHECK(IsConsistent());
235 if (!buffer_) {
236 if (capacity > 0) {
237 buffer_ = new RefCountedObject<Buffer>(0, capacity);
238 }
239 RTC_DCHECK(IsConsistent());
240 return;
241 } else if (capacity <= buffer_->capacity()) {
242 return;
243 }
244
245 CloneDataIfReferenced(std::max(buffer_->capacity(), capacity));
246 buffer_->EnsureCapacity(capacity);
247 RTC_DCHECK(IsConsistent());
248 }
249
250 // Resets the buffer to zero size and capacity.
251 void Clear() {
252 RTC_DCHECK(IsConsistent());
253 if (!buffer_ || !buffer_->HasOneRef()) {
254 buffer_ = nullptr;
255 } else {
256 buffer_->Clear();
257 }
258 RTC_DCHECK(IsConsistent());
259 }
260
261 // Swaps two buffers.
262 friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
263 std::swap(a.buffer_, b.buffer_);
264 }
265
266 private:
267 // Create a copy of the underlying data if it is referenced from other Buffer
268 // objects.
269 void CloneDataIfReferenced(size_t new_capacity) {
270 if (buffer_->HasOneRef()) {
271 return;
272 }
273
274 buffer_ = new RefCountedObject<Buffer>(buffer_->data(), buffer_->size(),
275 new_capacity);
276 RTC_DCHECK(IsConsistent());
277 }
278
279 // Pre- and postcondition of all methods.
280 bool IsConsistent() const {
281 return (!buffer_ || buffer_->capacity() > 0);
282 }
283
284 // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
285 scoped_refptr<RefCountedObject<Buffer>> buffer_;
286};
287
288} // namespace rtc
289
290#endif // WEBRTC_BASE_COPYONWRITEBUFFER_H_