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