blob: bdfe3937c724e759cd6620a1e0efb3d60e0b0255 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#ifndef RTC_BASE_COPYONWRITEBUFFER_H_
12#define RTC_BASE_COPYONWRITEBUFFER_H_
jbauch13041cf2016-02-25 06:16:52 -080013
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020014#include <algorithm>
15#include <utility>
jbauch13041cf2016-02-25 06:16:52 -080016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/buffer.h"
18#include "rtc_base/checks.h"
19#include "rtc_base/refcount.h"
20#include "rtc_base/scoped_ref_ptr.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020021
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*.
39 template <typename T,
40 typename std::enable_if<
41 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
42 CopyOnWriteBuffer(const T* data, size_t size)
43 : CopyOnWriteBuffer(data, size, size) {}
44 template <typename T,
45 typename std::enable_if<
46 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
47 CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
48 : CopyOnWriteBuffer(size, capacity) {
49 if (buffer_) {
50 std::memcpy(buffer_->data(), data, size);
51 }
52 }
53
54 // Construct a buffer from the contents of an array.
55 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
60 : 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>().
66 template <typename T = uint8_t,
67 typename std::enable_if<
68 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
69 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.
75 template <typename T = uint8_t,
76 typename std::enable_if<
77 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
78 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.
89 template <typename T = uint8_t,
90 typename std::enable_if<
91 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
92 const 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());
122 buffer_ = std::move(buf.buffer_);
123 return *this;
124 }
125
126 bool operator==(const CopyOnWriteBuffer& buf) const;
127
128 bool operator!=(const CopyOnWriteBuffer& buf) const {
129 return !(*this == buf);
130 }
131
132 uint8_t& operator[](size_t index) {
133 RTC_DCHECK_LT(index, size());
134 return data()[index];
135 }
136
137 uint8_t operator[](size_t index) const {
138 RTC_DCHECK_LT(index, size());
139 return cdata()[index];
140 }
141
142 // Replace the contents of the buffer. Accepts the same types as the
143 // constructors.
144 template <typename T,
145 typename std::enable_if<
146 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
147 void SetData(const T* data, size_t size) {
148 RTC_DCHECK(IsConsistent());
149 if (!buffer_) {
150 buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
151 } else if (!buffer_->HasOneRef()) {
152 buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
153 } else {
154 buffer_->SetData(data, size);
155 }
156 RTC_DCHECK(IsConsistent());
157 }
158
159 template <typename T,
160 size_t N,
161 typename std::enable_if<
162 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
163 void SetData(const T (&array)[N]) {
164 SetData(array, N);
165 }
166
167 void SetData(const CopyOnWriteBuffer& buf) {
168 RTC_DCHECK(IsConsistent());
169 RTC_DCHECK(buf.IsConsistent());
170 if (&buf != this) {
171 buffer_ = buf.buffer_;
172 }
173 }
174
175 // Append data to the buffer. Accepts the same types as the constructors.
176 template <typename T,
177 typename std::enable_if<
178 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
179 void AppendData(const T* data, size_t size) {
180 RTC_DCHECK(IsConsistent());
181 if (!buffer_) {
182 buffer_ = new RefCountedObject<Buffer>(data, size);
183 RTC_DCHECK(IsConsistent());
184 return;
185 }
186
187 CloneDataIfReferenced(std::max(buffer_->capacity(),
188 buffer_->size() + size));
189 buffer_->AppendData(data, size);
190 RTC_DCHECK(IsConsistent());
191 }
192
193 template <typename T,
194 size_t N,
195 typename std::enable_if<
196 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
197 void AppendData(const T (&array)[N]) {
198 AppendData(array, N);
199 }
200
201 void AppendData(const CopyOnWriteBuffer& buf) {
202 AppendData(buf.data(), buf.size());
203 }
204
205 // Sets the size of the buffer. If the new size is smaller than the old, the
206 // buffer contents will be kept but truncated; if the new size is greater,
207 // the existing contents will be kept and the new space will be
208 // uninitialized.
209 void SetSize(size_t size);
210
211 // Ensure that the buffer size can be increased to at least capacity without
212 // further reallocation. (Of course, this operation might need to reallocate
213 // the buffer.)
214 void EnsureCapacity(size_t capacity);
215
216 // Resets the buffer to zero size without altering capacity. Works even if the
217 // buffer has been moved from.
218 void Clear();
219
220 // Swaps two buffers.
221 friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
222 std::swap(a.buffer_, b.buffer_);
223 }
224
225 private:
226 // Create a copy of the underlying data if it is referenced from other Buffer
227 // objects.
228 void CloneDataIfReferenced(size_t new_capacity);
229
230 // Pre- and postcondition of all methods.
231 bool IsConsistent() const {
232 return (!buffer_ || buffer_->capacity() > 0);
233 }
234
235 // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
236 scoped_refptr<RefCountedObject<Buffer>> buffer_;
237};
238
239} // namespace rtc
jbauch13041cf2016-02-25 06:16:52 -0800240
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200241#endif // RTC_BASE_COPYONWRITEBUFFER_H_