blob: bc5e82f89f74cfa39dcd2241058e487d60643b60 [file] [log] [blame]
jbauch13041cf2016-02-25 14:16:521/*
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
Steve Anton10542f22019-01-11 17:11:0011#ifndef RTC_BASE_COPY_ON_WRITE_BUFFER_H_
12#define RTC_BASE_COPY_ON_WRITE_BUFFER_H_
jbauch13041cf2016-02-25 14:16:5213
Yves Gerey988cc082018-10-23 10:03:0114#include <stdint.h>
Jonas Olssona4d87372019-07-05 17:08:3315
Henrik Kjellanderec78f1c2017-06-29 05:52:5016#include <algorithm>
Yves Gerey988cc082018-10-23 10:03:0117#include <cstring>
18#include <string>
19#include <type_traits>
Henrik Kjellanderec78f1c2017-06-29 05:52:5020#include <utility>
jbauch13041cf2016-02-25 14:16:5221
Ali Tofighe00d7d02022-05-09 11:33:3622#include "absl/strings/string_view.h"
Mirko Bonadeid9708072019-01-25 19:26:4823#include "api/scoped_refptr.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3124#include "rtc_base/buffer.h"
25#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 17:11:0026#include "rtc_base/ref_counted_object.h"
Mirko Bonadei35214fc2019-09-23 12:54:2827#include "rtc_base/system/rtc_export.h"
philipelafc23772021-09-20 09:22:1328#include "rtc_base/type_traits.h"
Henrik Kjellanderec78f1c2017-06-29 05:52:5029
30namespace rtc {
31
Mirko Bonadei35214fc2019-09-23 12:54:2832class RTC_EXPORT CopyOnWriteBuffer {
Henrik Kjellanderec78f1c2017-06-29 05:52:5033 public:
34 // An empty buffer.
35 CopyOnWriteBuffer();
Amit Hilbuch45a2cd22019-03-12 23:58:0236 // Share the data with an existing buffer.
Henrik Kjellanderec78f1c2017-06-29 05:52:5037 CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
38 // Move contents from an existing buffer.
Danil Chapovalov26f72902023-07-06 09:06:3639 CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept;
Henrik Kjellanderec78f1c2017-06-29 05:52:5040
Jeroen de Borst4f6d2332018-07-18 18:25:1241 // Construct a buffer from a string, convenient for unittests.
Ali Tofighe00d7d02022-05-09 11:33:3642 explicit CopyOnWriteBuffer(absl::string_view s);
Jeroen de Borst4f6d2332018-07-18 18:25:1243
Henrik Kjellanderec78f1c2017-06-29 05:52:5044 // Construct a buffer with the specified number of uninitialized bytes.
45 explicit CopyOnWriteBuffer(size_t size);
46 CopyOnWriteBuffer(size_t size, size_t capacity);
47
48 // Construct a buffer and copy the specified number of bytes into it. The
49 // source array may be (const) uint8_t*, int8_t*, or char*.
50 template <typename T,
51 typename std::enable_if<
52 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
53 CopyOnWriteBuffer(const T* data, size_t size)
54 : CopyOnWriteBuffer(data, size, size) {}
55 template <typename T,
56 typename std::enable_if<
57 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
58 CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
59 : CopyOnWriteBuffer(size, capacity) {
60 if (buffer_) {
61 std::memcpy(buffer_->data(), data, size);
Ilya Nikolaevskiy741bab02019-09-25 12:37:1062 offset_ = 0;
63 size_ = size;
Henrik Kjellanderec78f1c2017-06-29 05:52:5064 }
65 }
66
67 // Construct a buffer from the contents of an array.
68 template <typename T,
69 size_t N,
70 typename std::enable_if<
71 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
72 CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
73 : CopyOnWriteBuffer(array, N) {}
74
philipelafc23772021-09-20 09:22:1375 // Construct a buffer from a vector like type.
76 template <typename VecT,
77 typename ElemT = typename std::remove_pointer_t<
78 decltype(std::declval<VecT>().data())>,
79 typename std::enable_if_t<
80 !std::is_same<VecT, CopyOnWriteBuffer>::value &&
81 HasDataAndSize<VecT, ElemT>::value &&
82 internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
83 explicit CopyOnWriteBuffer(const VecT& v)
84 : CopyOnWriteBuffer(v.data(), v.size()) {}
85
Harald Alvestrandb38d9d22023-08-04 09:50:3686 // Construct a buffer from a vector like type and a capacity argument
87 template <typename VecT,
88 typename ElemT = typename std::remove_pointer_t<
89 decltype(std::declval<VecT>().data())>,
90 typename std::enable_if_t<
91 !std::is_same<VecT, CopyOnWriteBuffer>::value &&
92 HasDataAndSize<VecT, ElemT>::value &&
93 internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
94 explicit CopyOnWriteBuffer(const VecT& v, size_t capacity)
95 : CopyOnWriteBuffer(v.data(), v.size(), capacity) {}
96
Henrik Kjellanderec78f1c2017-06-29 05:52:5097 ~CopyOnWriteBuffer();
98
99 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
100 // but you may also use .data<int8_t>() and .data<char>().
101 template <typename T = uint8_t,
102 typename std::enable_if<
103 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
104 const T* data() const {
105 return cdata<T>();
106 }
107
108 // Get writable pointer to the data. This will create a copy of the underlying
109 // data if it is shared with other buffers.
110 template <typename T = uint8_t,
111 typename std::enable_if<
112 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
Danil Chapovalov82e18752021-01-07 12:38:48113 T* MutableData() {
Henrik Kjellanderec78f1c2017-06-29 05:52:50114 RTC_DCHECK(IsConsistent());
115 if (!buffer_) {
116 return nullptr;
117 }
Ilya Nikolaevskiy741bab02019-09-25 12:37:10118 UnshareAndEnsureCapacity(capacity());
119 return buffer_->data<T>() + offset_;
Henrik Kjellanderec78f1c2017-06-29 05:52:50120 }
121
122 // Get const pointer to the data. This will not create a copy of the
123 // underlying data if it is shared with other buffers.
124 template <typename T = uint8_t,
125 typename std::enable_if<
126 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
127 const T* cdata() const {
128 RTC_DCHECK(IsConsistent());
129 if (!buffer_) {
130 return nullptr;
131 }
Ilya Nikolaevskiy741bab02019-09-25 12:37:10132 return buffer_->data<T>() + offset_;
Henrik Kjellanderec78f1c2017-06-29 05:52:50133 }
134
Danil Chapovalove0ec1252023-11-23 13:36:33135 bool empty() const { return size_ == 0; }
136
Henrik Kjellanderec78f1c2017-06-29 05:52:50137 size_t size() const {
138 RTC_DCHECK(IsConsistent());
Ilya Nikolaevskiy741bab02019-09-25 12:37:10139 return size_;
Henrik Kjellanderec78f1c2017-06-29 05:52:50140 }
141
142 size_t capacity() const {
143 RTC_DCHECK(IsConsistent());
Ilya Nikolaevskiy741bab02019-09-25 12:37:10144 return buffer_ ? buffer_->capacity() - offset_ : 0;
Henrik Kjellanderec78f1c2017-06-29 05:52:50145 }
146
147 CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
148 RTC_DCHECK(IsConsistent());
149 RTC_DCHECK(buf.IsConsistent());
150 if (&buf != this) {
151 buffer_ = buf.buffer_;
Ilya Nikolaevskiy741bab02019-09-25 12:37:10152 offset_ = buf.offset_;
153 size_ = buf.size_;
Henrik Kjellanderec78f1c2017-06-29 05:52:50154 }
155 return *this;
156 }
157
158 CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
159 RTC_DCHECK(IsConsistent());
160 RTC_DCHECK(buf.IsConsistent());
161 buffer_ = std::move(buf.buffer_);
Ilya Nikolaevskiy741bab02019-09-25 12:37:10162 offset_ = buf.offset_;
163 size_ = buf.size_;
164 buf.offset_ = 0;
165 buf.size_ = 0;
Henrik Kjellanderec78f1c2017-06-29 05:52:50166 return *this;
167 }
168
169 bool operator==(const CopyOnWriteBuffer& buf) const;
170
171 bool operator!=(const CopyOnWriteBuffer& buf) const {
172 return !(*this == buf);
173 }
174
Henrik Kjellanderec78f1c2017-06-29 05:52:50175 uint8_t operator[](size_t index) const {
176 RTC_DCHECK_LT(index, size());
177 return cdata()[index];
178 }
179
180 // Replace the contents of the buffer. Accepts the same types as the
181 // constructors.
182 template <typename T,
183 typename std::enable_if<
184 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
185 void SetData(const T* data, size_t size) {
186 RTC_DCHECK(IsConsistent());
187 if (!buffer_) {
Danil Chapovalov8df643b2021-01-22 15:11:10188 buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
Henrik Kjellanderec78f1c2017-06-29 05:52:50189 } else if (!buffer_->HasOneRef()) {
Danil Chapovalov8df643b2021-01-22 15:11:10190 buffer_ = new RefCountedBuffer(data, size, capacity());
Henrik Kjellanderec78f1c2017-06-29 05:52:50191 } else {
192 buffer_->SetData(data, size);
193 }
Ilya Nikolaevskiy741bab02019-09-25 12:37:10194 offset_ = 0;
195 size_ = size;
196
Henrik Kjellanderec78f1c2017-06-29 05:52:50197 RTC_DCHECK(IsConsistent());
198 }
199
200 template <typename T,
201 size_t N,
202 typename std::enable_if<
203 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
204 void SetData(const T (&array)[N]) {
205 SetData(array, N);
206 }
207
208 void SetData(const CopyOnWriteBuffer& buf) {
209 RTC_DCHECK(IsConsistent());
210 RTC_DCHECK(buf.IsConsistent());
211 if (&buf != this) {
212 buffer_ = buf.buffer_;
Ilya Nikolaevskiy741bab02019-09-25 12:37:10213 offset_ = buf.offset_;
214 size_ = buf.size_;
Henrik Kjellanderec78f1c2017-06-29 05:52:50215 }
216 }
217
218 // Append data to the buffer. Accepts the same types as the constructors.
219 template <typename T,
220 typename std::enable_if<
221 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
222 void AppendData(const T* data, size_t size) {
223 RTC_DCHECK(IsConsistent());
224 if (!buffer_) {
Danil Chapovalov8df643b2021-01-22 15:11:10225 buffer_ = new RefCountedBuffer(data, size);
Ilya Nikolaevskiy741bab02019-09-25 12:37:10226 offset_ = 0;
227 size_ = size;
Henrik Kjellanderec78f1c2017-06-29 05:52:50228 RTC_DCHECK(IsConsistent());
229 return;
230 }
231
Ilya Nikolaevskiy741bab02019-09-25 12:37:10232 UnshareAndEnsureCapacity(std::max(capacity(), size_ + size));
233
234 buffer_->SetSize(offset_ +
235 size_); // Remove data to the right of the slice.
Henrik Kjellanderec78f1c2017-06-29 05:52:50236 buffer_->AppendData(data, size);
Ilya Nikolaevskiy741bab02019-09-25 12:37:10237 size_ += size;
238
Henrik Kjellanderec78f1c2017-06-29 05:52:50239 RTC_DCHECK(IsConsistent());
240 }
241
242 template <typename T,
243 size_t N,
244 typename std::enable_if<
245 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
246 void AppendData(const T (&array)[N]) {
247 AppendData(array, N);
248 }
249
philipelafc23772021-09-20 09:22:13250 template <typename VecT,
251 typename ElemT = typename std::remove_pointer_t<
252 decltype(std::declval<VecT>().data())>,
253 typename std::enable_if_t<
254 HasDataAndSize<VecT, ElemT>::value &&
255 internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
256 void AppendData(const VecT& v) {
257 AppendData(v.data(), v.size());
Henrik Kjellanderec78f1c2017-06-29 05:52:50258 }
259
260 // Sets the size of the buffer. If the new size is smaller than the old, the
261 // buffer contents will be kept but truncated; if the new size is greater,
262 // the existing contents will be kept and the new space will be
263 // uninitialized.
264 void SetSize(size_t size);
265
266 // Ensure that the buffer size can be increased to at least capacity without
267 // further reallocation. (Of course, this operation might need to reallocate
268 // the buffer.)
269 void EnsureCapacity(size_t capacity);
270
271 // Resets the buffer to zero size without altering capacity. Works even if the
272 // buffer has been moved from.
273 void Clear();
274
275 // Swaps two buffers.
276 friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
Danil Chapovalov8df643b2021-01-22 15:11:10277 a.buffer_.swap(b.buffer_);
Ilya Nikolaevskiy741bab02019-09-25 12:37:10278 std::swap(a.offset_, b.offset_);
279 std::swap(a.size_, b.size_);
280 }
281
282 CopyOnWriteBuffer Slice(size_t offset, size_t length) const {
283 CopyOnWriteBuffer slice(*this);
284 RTC_DCHECK_LE(offset, size_);
285 RTC_DCHECK_LE(length + offset, size_);
286 slice.offset_ += offset;
287 slice.size_ = length;
288 return slice;
Henrik Kjellanderec78f1c2017-06-29 05:52:50289 }
290
291 private:
Danil Chapovalov8df643b2021-01-22 15:11:10292 using RefCountedBuffer = FinalRefCountedObject<Buffer>;
Henrik Kjellanderec78f1c2017-06-29 05:52:50293 // Create a copy of the underlying data if it is referenced from other Buffer
Ilya Nikolaevskiy741bab02019-09-25 12:37:10294 // objects or there is not enough capacity.
295 void UnshareAndEnsureCapacity(size_t new_capacity);
Henrik Kjellanderec78f1c2017-06-29 05:52:50296
297 // Pre- and postcondition of all methods.
Ilya Nikolaevskiy741bab02019-09-25 12:37:10298 bool IsConsistent() const {
299 if (buffer_) {
300 return buffer_->capacity() > 0 && offset_ <= buffer_->size() &&
301 offset_ + size_ <= buffer_->size();
302 } else {
303 return size_ == 0 && offset_ == 0;
304 }
305 }
Henrik Kjellanderec78f1c2017-06-29 05:52:50306
307 // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
Danil Chapovalov8df643b2021-01-22 15:11:10308 scoped_refptr<RefCountedBuffer> buffer_;
Ilya Nikolaevskiy741bab02019-09-25 12:37:10309 // This buffer may represent a slice of a original data.
310 size_t offset_; // Offset of a current slice in the original data in buffer_.
311 // Should be 0 if the buffer_ is empty.
312 size_t size_; // Size of a current slice in the original data in buffer_.
313 // Should be 0 if the buffer_ is empty.
Henrik Kjellanderec78f1c2017-06-29 05:52:50314};
315
316} // namespace rtc
jbauch13041cf2016-02-25 14:16:52317
Steve Anton10542f22019-01-11 17:11:00318#endif // RTC_BASE_COPY_ON_WRITE_BUFFER_H_