xref: /aosp_15_r20/external/webrtc/rtc_base/copy_on_write_buffer.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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 RTC_BASE_COPY_ON_WRITE_BUFFER_H_
12 #define RTC_BASE_COPY_ON_WRITE_BUFFER_H_
13 
14 #include <stdint.h>
15 
16 #include <algorithm>
17 #include <cstring>
18 #include <string>
19 #include <type_traits>
20 #include <utility>
21 
22 #include "absl/strings/string_view.h"
23 #include "api/scoped_refptr.h"
24 #include "rtc_base/buffer.h"
25 #include "rtc_base/checks.h"
26 #include "rtc_base/ref_counted_object.h"
27 #include "rtc_base/system/rtc_export.h"
28 #include "rtc_base/type_traits.h"
29 
30 namespace rtc {
31 
32 class RTC_EXPORT CopyOnWriteBuffer {
33  public:
34   // An empty buffer.
35   CopyOnWriteBuffer();
36   // Share the data with an existing buffer.
37   CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
38   // Move contents from an existing buffer.
39   CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
40 
41   // Construct a buffer from a string, convenient for unittests.
42   explicit CopyOnWriteBuffer(absl::string_view s);
43 
44   // 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>
CopyOnWriteBuffer(const T * data,size_t size)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>
CopyOnWriteBuffer(const T * data,size_t size,size_t capacity)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);
62       offset_ = 0;
63       size_ = size;
64     }
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>
CopyOnWriteBuffer(const T (& array)[N])72   CopyOnWriteBuffer(const T (&array)[N])  // NOLINT: runtime/explicit
73       : CopyOnWriteBuffer(array, N) {}
74 
75   // 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>
CopyOnWriteBuffer(const VecT & v)83   explicit CopyOnWriteBuffer(const VecT& v)
84       : CopyOnWriteBuffer(v.data(), v.size()) {}
85 
86   ~CopyOnWriteBuffer();
87 
88   // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
89   // but you may also use .data<int8_t>() and .data<char>().
90   template <typename T = uint8_t,
91             typename std::enable_if<
92                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
data()93   const T* data() const {
94     return cdata<T>();
95   }
96 
97   // Get writable pointer to the data. This will create a copy of the underlying
98   // data if it is shared with other buffers.
99   template <typename T = uint8_t,
100             typename std::enable_if<
101                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
MutableData()102   T* MutableData() {
103     RTC_DCHECK(IsConsistent());
104     if (!buffer_) {
105       return nullptr;
106     }
107     UnshareAndEnsureCapacity(capacity());
108     return buffer_->data<T>() + offset_;
109   }
110 
111   // Get const pointer to the data. This will not create a copy of the
112   // underlying data if it is shared with other buffers.
113   template <typename T = uint8_t,
114             typename std::enable_if<
115                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
cdata()116   const T* cdata() const {
117     RTC_DCHECK(IsConsistent());
118     if (!buffer_) {
119       return nullptr;
120     }
121     return buffer_->data<T>() + offset_;
122   }
123 
size()124   size_t size() const {
125     RTC_DCHECK(IsConsistent());
126     return size_;
127   }
128 
capacity()129   size_t capacity() const {
130     RTC_DCHECK(IsConsistent());
131     return buffer_ ? buffer_->capacity() - offset_ : 0;
132   }
133 
134   CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
135     RTC_DCHECK(IsConsistent());
136     RTC_DCHECK(buf.IsConsistent());
137     if (&buf != this) {
138       buffer_ = buf.buffer_;
139       offset_ = buf.offset_;
140       size_ = buf.size_;
141     }
142     return *this;
143   }
144 
145   CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
146     RTC_DCHECK(IsConsistent());
147     RTC_DCHECK(buf.IsConsistent());
148     buffer_ = std::move(buf.buffer_);
149     offset_ = buf.offset_;
150     size_ = buf.size_;
151     buf.offset_ = 0;
152     buf.size_ = 0;
153     return *this;
154   }
155 
156   bool operator==(const CopyOnWriteBuffer& buf) const;
157 
158   bool operator!=(const CopyOnWriteBuffer& buf) const {
159     return !(*this == buf);
160   }
161 
162   uint8_t operator[](size_t index) const {
163     RTC_DCHECK_LT(index, size());
164     return cdata()[index];
165   }
166 
167   // Replace the contents of the buffer. Accepts the same types as the
168   // constructors.
169   template <typename T,
170             typename std::enable_if<
171                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
SetData(const T * data,size_t size)172   void SetData(const T* data, size_t size) {
173     RTC_DCHECK(IsConsistent());
174     if (!buffer_) {
175       buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
176     } else if (!buffer_->HasOneRef()) {
177       buffer_ = new RefCountedBuffer(data, size, capacity());
178     } else {
179       buffer_->SetData(data, size);
180     }
181     offset_ = 0;
182     size_ = size;
183 
184     RTC_DCHECK(IsConsistent());
185   }
186 
187   template <typename T,
188             size_t N,
189             typename std::enable_if<
190                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
SetData(const T (& array)[N])191   void SetData(const T (&array)[N]) {
192     SetData(array, N);
193   }
194 
SetData(const CopyOnWriteBuffer & buf)195   void SetData(const CopyOnWriteBuffer& buf) {
196     RTC_DCHECK(IsConsistent());
197     RTC_DCHECK(buf.IsConsistent());
198     if (&buf != this) {
199       buffer_ = buf.buffer_;
200       offset_ = buf.offset_;
201       size_ = buf.size_;
202     }
203   }
204 
205   // Append data to the buffer. Accepts the same types as the constructors.
206   template <typename T,
207             typename std::enable_if<
208                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
AppendData(const T * data,size_t size)209   void AppendData(const T* data, size_t size) {
210     RTC_DCHECK(IsConsistent());
211     if (!buffer_) {
212       buffer_ = new RefCountedBuffer(data, size);
213       offset_ = 0;
214       size_ = size;
215       RTC_DCHECK(IsConsistent());
216       return;
217     }
218 
219     UnshareAndEnsureCapacity(std::max(capacity(), size_ + size));
220 
221     buffer_->SetSize(offset_ +
222                      size_);  // Remove data to the right of the slice.
223     buffer_->AppendData(data, size);
224     size_ += size;
225 
226     RTC_DCHECK(IsConsistent());
227   }
228 
229   template <typename T,
230             size_t N,
231             typename std::enable_if<
232                 internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
AppendData(const T (& array)[N])233   void AppendData(const T (&array)[N]) {
234     AppendData(array, N);
235   }
236 
237   template <typename VecT,
238             typename ElemT = typename std::remove_pointer_t<
239                 decltype(std::declval<VecT>().data())>,
240             typename std::enable_if_t<
241                 HasDataAndSize<VecT, ElemT>::value &&
242                 internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
AppendData(const VecT & v)243   void AppendData(const VecT& v) {
244     AppendData(v.data(), v.size());
245   }
246 
247   // Sets the size of the buffer. If the new size is smaller than the old, the
248   // buffer contents will be kept but truncated; if the new size is greater,
249   // the existing contents will be kept and the new space will be
250   // uninitialized.
251   void SetSize(size_t size);
252 
253   // Ensure that the buffer size can be increased to at least capacity without
254   // further reallocation. (Of course, this operation might need to reallocate
255   // the buffer.)
256   void EnsureCapacity(size_t capacity);
257 
258   // Resets the buffer to zero size without altering capacity. Works even if the
259   // buffer has been moved from.
260   void Clear();
261 
262   // Swaps two buffers.
swap(CopyOnWriteBuffer & a,CopyOnWriteBuffer & b)263   friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
264     a.buffer_.swap(b.buffer_);
265     std::swap(a.offset_, b.offset_);
266     std::swap(a.size_, b.size_);
267   }
268 
Slice(size_t offset,size_t length)269   CopyOnWriteBuffer Slice(size_t offset, size_t length) const {
270     CopyOnWriteBuffer slice(*this);
271     RTC_DCHECK_LE(offset, size_);
272     RTC_DCHECK_LE(length + offset, size_);
273     slice.offset_ += offset;
274     slice.size_ = length;
275     return slice;
276   }
277 
278  private:
279   using RefCountedBuffer = FinalRefCountedObject<Buffer>;
280   // Create a copy of the underlying data if it is referenced from other Buffer
281   // objects or there is not enough capacity.
282   void UnshareAndEnsureCapacity(size_t new_capacity);
283 
284   // Pre- and postcondition of all methods.
IsConsistent()285   bool IsConsistent() const {
286     if (buffer_) {
287       return buffer_->capacity() > 0 && offset_ <= buffer_->size() &&
288              offset_ + size_ <= buffer_->size();
289     } else {
290       return size_ == 0 && offset_ == 0;
291     }
292   }
293 
294   // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
295   scoped_refptr<RefCountedBuffer> buffer_;
296   // This buffer may represent a slice of a original data.
297   size_t offset_;  // Offset of a current slice in the original data in buffer_.
298                    // Should be 0 if the buffer_ is empty.
299   size_t size_;    // Size of a current slice in the original data in buffer_.
300                    // Should be 0 if the buffer_ is empty.
301 };
302 
303 }  // namespace rtc
304 
305 #endif  // RTC_BASE_COPY_ON_WRITE_BUFFER_H_
306