OLD | NEW |
(Empty) | |
| 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 WEBRTC_BASE_COPYONWRITEBUFFER_H_ |
| 12 #define WEBRTC_BASE_COPYONWRITEBUFFER_H_ |
| 13 |
| 14 #include <algorithm> |
| 15 #include <utility> |
| 16 |
| 17 #include "webrtc/base/buffer.h" |
| 18 #include "webrtc/base/checks.h" |
| 19 #include "webrtc/base/refcount.h" |
| 20 #include "webrtc/base/scoped_ref_ptr.h" |
| 21 |
| 22 namespace rtc { |
| 23 |
| 24 class CopyOnWriteBuffer { |
| 25 public: |
| 26 // An empty buffer. |
| 27 CopyOnWriteBuffer(); |
| 28 // Copy size and contents of an existing buffer. |
| 29 CopyOnWriteBuffer(const CopyOnWriteBuffer& buf); |
| 30 // Move contents from an existing buffer. |
| 31 CopyOnWriteBuffer(CopyOnWriteBuffer&& buf); |
| 32 |
| 33 // Construct a buffer with the specified number of uninitialized bytes. |
| 34 explicit CopyOnWriteBuffer(size_t size); |
| 35 CopyOnWriteBuffer(size_t size, size_t capacity); |
| 36 |
| 37 // Construct a buffer and copy the specified number of bytes into it. The |
| 38 // source array may be (const) uint8_t*, int8_t*, or char*. |
| 39 template <typename T, typename internal::ByteType<T>::t = 0> |
| 40 CopyOnWriteBuffer(const T* data, size_t size) |
| 41 : CopyOnWriteBuffer(data, size, size) {} |
| 42 template <typename T, typename internal::ByteType<T>::t = 0> |
| 43 CopyOnWriteBuffer(const T* data, size_t size, size_t capacity) |
| 44 : CopyOnWriteBuffer(size, capacity) { |
| 45 std::memcpy(buffer_->data(), data, size); |
| 46 } |
| 47 |
| 48 // Construct a buffer from the contents of an array. |
| 49 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 50 CopyOnWriteBuffer(const T(&array)[N]) // NOLINT: runtime/explicit |
| 51 : CopyOnWriteBuffer(array, N) {} |
| 52 |
| 53 ~CopyOnWriteBuffer(); |
| 54 |
| 55 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, |
| 56 // but you may also use .data<int8_t>() and .data<char>(). |
| 57 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| 58 const T* data() const { |
| 59 return cdata<T>(); |
| 60 } |
| 61 |
| 62 // Get writable pointer to the data. This will create a copy of the underlying |
| 63 // data if it is shared with other buffers. |
| 64 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| 65 T* data() { |
| 66 RTC_DCHECK(IsConsistent()); |
| 67 if (!buffer_) { |
| 68 return nullptr; |
| 69 } |
| 70 CloneDataIfReferenced(buffer_->capacity()); |
| 71 return buffer_->data<T>(); |
| 72 } |
| 73 |
| 74 // Get const pointer to the data. This will not create a copy of the |
| 75 // underlying data if it is shared with other buffers. |
| 76 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| 77 T* cdata() const { |
| 78 RTC_DCHECK(IsConsistent()); |
| 79 if (!buffer_) { |
| 80 return nullptr; |
| 81 } |
| 82 return buffer_->data<T>(); |
| 83 } |
| 84 |
| 85 size_t size() const { |
| 86 RTC_DCHECK(IsConsistent()); |
| 87 return buffer_ ? buffer_->size() : 0; |
| 88 } |
| 89 |
| 90 size_t capacity() const { |
| 91 RTC_DCHECK(IsConsistent()); |
| 92 return buffer_ ? buffer_->capacity() : 0; |
| 93 } |
| 94 |
| 95 CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) { |
| 96 RTC_DCHECK(IsConsistent()); |
| 97 RTC_DCHECK(buf.IsConsistent()); |
| 98 if (&buf != this) { |
| 99 buffer_ = buf.buffer_; |
| 100 } |
| 101 return *this; |
| 102 } |
| 103 |
| 104 CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { |
| 105 RTC_DCHECK(IsConsistent()); |
| 106 RTC_DCHECK(buf.IsConsistent()); |
| 107 // TODO(jbauch): use std::move once scoped_refptr supports it (issue 5556) |
| 108 buffer_.swap(buf.buffer_); |
| 109 buf.buffer_ = nullptr; |
| 110 return *this; |
| 111 } |
| 112 |
| 113 bool operator==(const CopyOnWriteBuffer& buf) const { |
| 114 // Must either use the same buffer internally or have the same contents. |
| 115 RTC_DCHECK(IsConsistent()); |
| 116 RTC_DCHECK(buf.IsConsistent()); |
| 117 return buffer_.get() == buf.buffer_.get() || |
| 118 (buffer_.get() && buf.buffer_.get() && |
| 119 *buffer_.get() == *buf.buffer_.get()); |
| 120 } |
| 121 |
| 122 bool operator!=(const CopyOnWriteBuffer& buf) const { |
| 123 return !(*this == buf); |
| 124 } |
| 125 |
| 126 // Replace the contents of the buffer. Accepts the same types as the |
| 127 // constructors. |
| 128 template <typename T, typename internal::ByteType<T>::t = 0> |
| 129 void SetData(const T* data, size_t size) { |
| 130 RTC_DCHECK(IsConsistent()); |
| 131 if (!buffer_ || !buffer_->HasOneRef()) { |
| 132 buffer_ = new RefCountedObject<Buffer>(data, size, size); |
| 133 } else { |
| 134 buffer_->SetData(data, size); |
| 135 } |
| 136 RTC_DCHECK(IsConsistent()); |
| 137 } |
| 138 |
| 139 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 140 void SetData(const T(&array)[N]) { |
| 141 SetData(array, N); |
| 142 } |
| 143 |
| 144 void SetData(const CopyOnWriteBuffer& buf) { |
| 145 RTC_DCHECK(IsConsistent()); |
| 146 RTC_DCHECK(buf.IsConsistent()); |
| 147 if (&buf != this) { |
| 148 buffer_ = buf.buffer_; |
| 149 } |
| 150 } |
| 151 |
| 152 // Append data to the buffer. Accepts the same types as the constructors. |
| 153 template <typename T, typename internal::ByteType<T>::t = 0> |
| 154 void AppendData(const T* data, size_t size) { |
| 155 RTC_DCHECK(IsConsistent()); |
| 156 if (!buffer_) { |
| 157 buffer_ = new RefCountedObject<Buffer>(data, size); |
| 158 RTC_DCHECK(IsConsistent()); |
| 159 return; |
| 160 } |
| 161 |
| 162 CloneDataIfReferenced(std::max(buffer_->capacity(), |
| 163 buffer_->size() + size)); |
| 164 buffer_->AppendData(data, size); |
| 165 RTC_DCHECK(IsConsistent()); |
| 166 } |
| 167 |
| 168 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 169 void AppendData(const T(&array)[N]) { |
| 170 AppendData(array, N); |
| 171 } |
| 172 |
| 173 void AppendData(const CopyOnWriteBuffer& buf) { |
| 174 AppendData(buf.data(), buf.size()); |
| 175 } |
| 176 |
| 177 // Sets the size of the buffer. If the new size is smaller than the old, the |
| 178 // buffer contents will be kept but truncated; if the new size is greater, |
| 179 // the existing contents will be kept and the new space will be |
| 180 // uninitialized. |
| 181 void SetSize(size_t size) { |
| 182 RTC_DCHECK(IsConsistent()); |
| 183 if (!buffer_) { |
| 184 if (size > 0) { |
| 185 buffer_ = new RefCountedObject<Buffer>(size); |
| 186 } |
| 187 RTC_DCHECK(IsConsistent()); |
| 188 return; |
| 189 } |
| 190 |
| 191 CloneDataIfReferenced(std::max(buffer_->capacity(), size)); |
| 192 buffer_->SetSize(size); |
| 193 RTC_DCHECK(IsConsistent()); |
| 194 } |
| 195 |
| 196 // Ensure that the buffer size can be increased to at least capacity without |
| 197 // further reallocation. (Of course, this operation might need to reallocate |
| 198 // the buffer.) |
| 199 void EnsureCapacity(size_t capacity) { |
| 200 RTC_DCHECK(IsConsistent()); |
| 201 if (!buffer_) { |
| 202 if (capacity > 0) { |
| 203 buffer_ = new RefCountedObject<Buffer>(0, capacity); |
| 204 } |
| 205 RTC_DCHECK(IsConsistent()); |
| 206 return; |
| 207 } else if (capacity <= buffer_->capacity()) { |
| 208 return; |
| 209 } |
| 210 |
| 211 CloneDataIfReferenced(std::max(buffer_->capacity(), capacity)); |
| 212 buffer_->EnsureCapacity(capacity); |
| 213 RTC_DCHECK(IsConsistent()); |
| 214 } |
| 215 |
| 216 // Resets the buffer to zero size and capacity. |
| 217 void Clear() { |
| 218 RTC_DCHECK(IsConsistent()); |
| 219 if (!buffer_ || !buffer_->HasOneRef()) { |
| 220 buffer_ = nullptr; |
| 221 } else { |
| 222 buffer_->Clear(); |
| 223 } |
| 224 RTC_DCHECK(IsConsistent()); |
| 225 } |
| 226 |
| 227 // Swaps two buffers. |
| 228 friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) { |
| 229 std::swap(a.buffer_, b.buffer_); |
| 230 } |
| 231 |
| 232 private: |
| 233 // Create a copy of the underlying data if it is referenced from other Buffer |
| 234 // objects. |
| 235 void CloneDataIfReferenced(size_t new_capacity) { |
| 236 if (buffer_->HasOneRef()) { |
| 237 return; |
| 238 } |
| 239 |
| 240 buffer_ = new RefCountedObject<Buffer>(buffer_->data(), buffer_->size(), |
| 241 new_capacity); |
| 242 RTC_DCHECK(IsConsistent()); |
| 243 } |
| 244 |
| 245 // Pre- and postcondition of all methods. |
| 246 bool IsConsistent() const { |
| 247 return (!buffer_ || buffer_->capacity() > 0); |
| 248 } |
| 249 |
| 250 // buffer_ is either null, or points to an rtc::Buffer with capacity > 0. |
| 251 scoped_refptr<RefCountedObject<Buffer>> buffer_; |
| 252 }; |
| 253 |
| 254 } // namespace rtc |
| 255 |
| 256 #endif // WEBRTC_BASE_COPYONWRITEBUFFER_H_ |
OLD | NEW |