blob: 0514e2fe6611a25966fd633e8f692f4623a2f5cf [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"
Niels Möller84255bb2017-10-06 13:43:23 +020020#include "rtc_base/refcountedobject.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/scoped_ref_ptr.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020022
23namespace rtc {
24
25class CopyOnWriteBuffer {
26 public:
27 // An empty buffer.
28 CopyOnWriteBuffer();
29 // Copy size and contents of an existing buffer.
30 CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
31 // Move contents from an existing buffer.
32 CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
33
Jeroen de Borst4f6d2332018-07-18 11:25:12 -070034 // Construct a buffer from a string, convenient for unittests.
35 CopyOnWriteBuffer(const std::string& s);
36
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020037 // Construct a buffer with the specified number of uninitialized bytes.
38 explicit CopyOnWriteBuffer(size_t size);
39 CopyOnWriteBuffer(size_t size, size_t capacity);
40
41 // Construct a buffer and copy the specified number of bytes into it. The
42 // source array may be (const) uint8_t*, int8_t*, or char*.
43 template <typename T,
44 typename std::enable_if<
45 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
46 CopyOnWriteBuffer(const T* data, size_t size)
47 : CopyOnWriteBuffer(data, size, size) {}
48 template <typename T,
49 typename std::enable_if<
50 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
51 CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
52 : CopyOnWriteBuffer(size, capacity) {
53 if (buffer_) {
54 std::memcpy(buffer_->data(), data, size);
55 }
56 }
57
58 // Construct a buffer from the contents of an array.
59 template <typename T,
60 size_t N,
61 typename std::enable_if<
62 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
63 CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
64 : CopyOnWriteBuffer(array, N) {}
65
66 ~CopyOnWriteBuffer();
67
68 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
69 // but you may also use .data<int8_t>() and .data<char>().
70 template <typename T = uint8_t,
71 typename std::enable_if<
72 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
73 const T* data() const {
74 return cdata<T>();
75 }
76
77 // Get writable pointer to the data. This will create a copy of the underlying
78 // data if it is shared with other buffers.
79 template <typename T = uint8_t,
80 typename std::enable_if<
81 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
82 T* data() {
83 RTC_DCHECK(IsConsistent());
84 if (!buffer_) {
85 return nullptr;
86 }
87 CloneDataIfReferenced(buffer_->capacity());
88 return buffer_->data<T>();
89 }
90
91 // Get const pointer to the data. This will not create a copy of the
92 // underlying data if it is shared with other buffers.
93 template <typename T = uint8_t,
94 typename std::enable_if<
95 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
96 const T* cdata() const {
97 RTC_DCHECK(IsConsistent());
98 if (!buffer_) {
99 return nullptr;
100 }
101 return buffer_->data<T>();
102 }
103
104 size_t size() const {
105 RTC_DCHECK(IsConsistent());
106 return buffer_ ? buffer_->size() : 0;
107 }
108
109 size_t capacity() const {
110 RTC_DCHECK(IsConsistent());
111 return buffer_ ? buffer_->capacity() : 0;
112 }
113
114 CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
115 RTC_DCHECK(IsConsistent());
116 RTC_DCHECK(buf.IsConsistent());
117 if (&buf != this) {
118 buffer_ = buf.buffer_;
119 }
120 return *this;
121 }
122
123 CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
124 RTC_DCHECK(IsConsistent());
125 RTC_DCHECK(buf.IsConsistent());
126 buffer_ = std::move(buf.buffer_);
127 return *this;
128 }
129
130 bool operator==(const CopyOnWriteBuffer& buf) const;
131
132 bool operator!=(const CopyOnWriteBuffer& buf) const {
133 return !(*this == buf);
134 }
135
136 uint8_t& operator[](size_t index) {
137 RTC_DCHECK_LT(index, size());
138 return data()[index];
139 }
140
141 uint8_t operator[](size_t index) const {
142 RTC_DCHECK_LT(index, size());
143 return cdata()[index];
144 }
145
146 // Replace the contents of the buffer. Accepts the same types as the
147 // constructors.
148 template <typename T,
149 typename std::enable_if<
150 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
151 void SetData(const T* data, size_t size) {
152 RTC_DCHECK(IsConsistent());
153 if (!buffer_) {
154 buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
155 } else if (!buffer_->HasOneRef()) {
156 buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
157 } else {
158 buffer_->SetData(data, size);
159 }
160 RTC_DCHECK(IsConsistent());
161 }
162
163 template <typename T,
164 size_t N,
165 typename std::enable_if<
166 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
167 void SetData(const T (&array)[N]) {
168 SetData(array, N);
169 }
170
171 void SetData(const CopyOnWriteBuffer& buf) {
172 RTC_DCHECK(IsConsistent());
173 RTC_DCHECK(buf.IsConsistent());
174 if (&buf != this) {
175 buffer_ = buf.buffer_;
176 }
177 }
178
179 // Append data to the buffer. Accepts the same types as the constructors.
180 template <typename T,
181 typename std::enable_if<
182 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
183 void AppendData(const T* data, size_t size) {
184 RTC_DCHECK(IsConsistent());
185 if (!buffer_) {
186 buffer_ = new RefCountedObject<Buffer>(data, size);
187 RTC_DCHECK(IsConsistent());
188 return;
189 }
190
Yves Gerey665174f2018-06-19 15:03:05 +0200191 CloneDataIfReferenced(
192 std::max(buffer_->capacity(), buffer_->size() + size));
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200193 buffer_->AppendData(data, size);
194 RTC_DCHECK(IsConsistent());
195 }
196
197 template <typename T,
198 size_t N,
199 typename std::enable_if<
200 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
201 void AppendData(const T (&array)[N]) {
202 AppendData(array, N);
203 }
204
205 void AppendData(const CopyOnWriteBuffer& buf) {
206 AppendData(buf.data(), buf.size());
207 }
208
209 // Sets the size of the buffer. If the new size is smaller than the old, the
210 // buffer contents will be kept but truncated; if the new size is greater,
211 // the existing contents will be kept and the new space will be
212 // uninitialized.
213 void SetSize(size_t size);
214
215 // Ensure that the buffer size can be increased to at least capacity without
216 // further reallocation. (Of course, this operation might need to reallocate
217 // the buffer.)
218 void EnsureCapacity(size_t capacity);
219
220 // Resets the buffer to zero size without altering capacity. Works even if the
221 // buffer has been moved from.
222 void Clear();
223
224 // Swaps two buffers.
225 friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
226 std::swap(a.buffer_, b.buffer_);
227 }
228
229 private:
230 // Create a copy of the underlying data if it is referenced from other Buffer
231 // objects.
232 void CloneDataIfReferenced(size_t new_capacity);
233
234 // Pre- and postcondition of all methods.
Yves Gerey665174f2018-06-19 15:03:05 +0200235 bool IsConsistent() const { return (!buffer_ || buffer_->capacity() > 0); }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200236
237 // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
238 scoped_refptr<RefCountedObject<Buffer>> buffer_;
239};
240
241} // namespace rtc
jbauch13041cf2016-02-25 06:16:52 -0800242
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200243#endif // RTC_BASE_COPYONWRITEBUFFER_H_