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