Index: webrtc/base/copyonwritebuffer.h |
diff --git a/webrtc/base/copyonwritebuffer.h b/webrtc/base/copyonwritebuffer.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..17f2710f3ecbd404646268d4af9a003d7c2e5465 |
--- /dev/null |
+++ b/webrtc/base/copyonwritebuffer.h |
@@ -0,0 +1,256 @@ |
+/* |
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#ifndef WEBRTC_BASE_COPYONWRITEBUFFER_H_ |
+#define WEBRTC_BASE_COPYONWRITEBUFFER_H_ |
+ |
+#include <algorithm> |
+#include <utility> |
+ |
+#include "webrtc/base/buffer.h" |
+#include "webrtc/base/checks.h" |
+#include "webrtc/base/refcount.h" |
+#include "webrtc/base/scoped_ref_ptr.h" |
+ |
+namespace rtc { |
+ |
+class CopyOnWriteBuffer { |
+ public: |
+ // An empty buffer. |
+ CopyOnWriteBuffer(); |
+ // Copy size and contents of an existing buffer. |
+ CopyOnWriteBuffer(const CopyOnWriteBuffer& buf); |
+ // Move contents from an existing buffer. |
+ CopyOnWriteBuffer(CopyOnWriteBuffer&& buf); |
+ |
+ // Construct a buffer with the specified number of uninitialized bytes. |
+ explicit CopyOnWriteBuffer(size_t size); |
+ CopyOnWriteBuffer(size_t size, size_t capacity); |
+ |
+ // Construct a buffer and copy the specified number of bytes into it. The |
+ // source array may be (const) uint8_t*, int8_t*, or char*. |
+ template <typename T, typename internal::ByteType<T>::t = 0> |
+ CopyOnWriteBuffer(const T* data, size_t size) |
+ : CopyOnWriteBuffer(data, size, size) {} |
+ template <typename T, typename internal::ByteType<T>::t = 0> |
+ CopyOnWriteBuffer(const T* data, size_t size, size_t capacity) |
+ : CopyOnWriteBuffer(size, capacity) { |
+ std::memcpy(buffer_->data(), data, size); |
+ } |
+ |
+ // Construct a buffer from the contents of an array. |
+ template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
+ CopyOnWriteBuffer(const T(&array)[N]) // NOLINT: runtime/explicit |
+ : CopyOnWriteBuffer(array, N) {} |
+ |
+ ~CopyOnWriteBuffer(); |
+ |
+ // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, |
+ // but you may also use .data<int8_t>() and .data<char>(). |
+ template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
+ const T* data() const { |
+ return cdata<T>(); |
+ } |
+ |
+ // Get writable pointer to the data. This will create a copy of the underlying |
+ // data if it is shared with other buffers. |
+ template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
+ T* data() { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_) { |
+ return nullptr; |
+ } |
+ CloneDataIfReferenced(buffer_->capacity()); |
+ return buffer_->data<T>(); |
+ } |
+ |
+ // Get const pointer to the data. This will not create a copy of the |
+ // underlying data if it is shared with other buffers. |
+ template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
+ T* cdata() const { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_) { |
+ return nullptr; |
+ } |
+ return buffer_->data<T>(); |
+ } |
+ |
+ size_t size() const { |
+ RTC_DCHECK(IsConsistent()); |
+ return buffer_ ? buffer_->size() : 0; |
+ } |
+ |
+ size_t capacity() const { |
+ RTC_DCHECK(IsConsistent()); |
+ return buffer_ ? buffer_->capacity() : 0; |
+ } |
+ |
+ CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) { |
+ RTC_DCHECK(IsConsistent()); |
+ RTC_DCHECK(buf.IsConsistent()); |
+ if (&buf != this) { |
+ buffer_ = buf.buffer_; |
+ } |
+ return *this; |
+ } |
+ |
+ CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { |
+ RTC_DCHECK(IsConsistent()); |
+ RTC_DCHECK(buf.IsConsistent()); |
+ // TODO(jbauch): use std::move once scoped_refptr supports it (issue 5556) |
+ buffer_.swap(buf.buffer_); |
+ buf.buffer_ = nullptr; |
+ return *this; |
+ } |
+ |
+ bool operator==(const CopyOnWriteBuffer& buf) const { |
+ // Must either use the same buffer internally or have the same contents. |
+ RTC_DCHECK(IsConsistent()); |
+ RTC_DCHECK(buf.IsConsistent()); |
+ return buffer_.get() == buf.buffer_.get() || |
+ (buffer_.get() && buf.buffer_.get() && |
+ *buffer_.get() == *buf.buffer_.get()); |
+ } |
+ |
+ bool operator!=(const CopyOnWriteBuffer& buf) const { |
+ return !(*this == buf); |
+ } |
+ |
+ // Replace the contents of the buffer. Accepts the same types as the |
+ // constructors. |
+ template <typename T, typename internal::ByteType<T>::t = 0> |
+ void SetData(const T* data, size_t size) { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_ || !buffer_->HasOneRef()) { |
+ buffer_ = new RefCountedObject<Buffer>(data, size, size); |
+ } else { |
+ buffer_->SetData(data, size); |
+ } |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
+ void SetData(const T(&array)[N]) { |
+ SetData(array, N); |
+ } |
+ |
+ void SetData(const CopyOnWriteBuffer& buf) { |
+ RTC_DCHECK(IsConsistent()); |
+ RTC_DCHECK(buf.IsConsistent()); |
+ if (&buf != this) { |
+ buffer_ = buf.buffer_; |
+ } |
+ } |
+ |
+ // Append data to the buffer. Accepts the same types as the constructors. |
+ template <typename T, typename internal::ByteType<T>::t = 0> |
+ void AppendData(const T* data, size_t size) { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_) { |
+ buffer_ = new RefCountedObject<Buffer>(data, size); |
+ RTC_DCHECK(IsConsistent()); |
+ return; |
+ } |
+ |
+ CloneDataIfReferenced(std::max(buffer_->capacity(), |
+ buffer_->size() + size)); |
+ buffer_->AppendData(data, size); |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
+ void AppendData(const T(&array)[N]) { |
+ AppendData(array, N); |
+ } |
+ |
+ void AppendData(const CopyOnWriteBuffer& buf) { |
+ AppendData(buf.data(), buf.size()); |
+ } |
+ |
+ // Sets the size of the buffer. If the new size is smaller than the old, the |
+ // buffer contents will be kept but truncated; if the new size is greater, |
+ // the existing contents will be kept and the new space will be |
+ // uninitialized. |
+ void SetSize(size_t size) { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_) { |
+ if (size > 0) { |
+ buffer_ = new RefCountedObject<Buffer>(size); |
+ } |
+ RTC_DCHECK(IsConsistent()); |
+ return; |
+ } |
+ |
+ CloneDataIfReferenced(std::max(buffer_->capacity(), size)); |
+ buffer_->SetSize(size); |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ // Ensure that the buffer size can be increased to at least capacity without |
+ // further reallocation. (Of course, this operation might need to reallocate |
+ // the buffer.) |
+ void EnsureCapacity(size_t capacity) { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_) { |
+ if (capacity > 0) { |
+ buffer_ = new RefCountedObject<Buffer>(0, capacity); |
+ } |
+ RTC_DCHECK(IsConsistent()); |
+ return; |
+ } else if (capacity <= buffer_->capacity()) { |
+ return; |
+ } |
+ |
+ CloneDataIfReferenced(std::max(buffer_->capacity(), capacity)); |
+ buffer_->EnsureCapacity(capacity); |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ // Resets the buffer to zero size and capacity. |
+ void Clear() { |
+ RTC_DCHECK(IsConsistent()); |
+ if (!buffer_ || !buffer_->HasOneRef()) { |
+ buffer_ = nullptr; |
+ } else { |
+ buffer_->Clear(); |
+ } |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ // Swaps two buffers. |
+ friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) { |
+ std::swap(a.buffer_, b.buffer_); |
+ } |
+ |
+ private: |
+ // Create a copy of the underlying data if it is referenced from other Buffer |
+ // objects. |
+ void CloneDataIfReferenced(size_t new_capacity) { |
+ if (buffer_->HasOneRef()) { |
+ return; |
+ } |
+ |
+ buffer_ = new RefCountedObject<Buffer>(buffer_->data(), buffer_->size(), |
+ new_capacity); |
+ RTC_DCHECK(IsConsistent()); |
+ } |
+ |
+ // Pre- and postcondition of all methods. |
+ bool IsConsistent() const { |
+ return (!buffer_ || buffer_->capacity() > 0); |
+ } |
+ |
+ // buffer_ is either null, or points to an rtc::Buffer with capacity > 0. |
+ scoped_refptr<RefCountedObject<Buffer>> buffer_; |
+}; |
+ |
+} // namespace rtc |
+ |
+#endif // WEBRTC_BASE_COPYONWRITEBUFFER_H_ |