Add CopyOnWriteBuffer class

This CL introduces a new class CopyOnWriteBuffer that holds data in a
refcounted Buffer which is shared between copied CopyOnWriteBuffer to avoid
unnecessary allocations / memory copies.

BUG=webrtc:5155

Review URL: https://codereview.webrtc.org/1697743003

Cr-Commit-Position: refs/heads/master@{#11767}
diff --git a/webrtc/base/copyonwritebuffer.h b/webrtc/base/copyonwritebuffer.h
new file mode 100644
index 0000000..17f2710
--- /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_