|  | /* | 
|  | *  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 RTC_BASE_COPY_ON_WRITE_BUFFER_H_ | 
|  | #define RTC_BASE_COPY_ON_WRITE_BUFFER_H_ | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstring> | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  | #include <utility> | 
|  |  | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "rtc_base/buffer.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/ref_counted_object.h" | 
|  | #include "rtc_base/system/rtc_export.h" | 
|  |  | 
|  | namespace rtc { | 
|  |  | 
|  | class RTC_EXPORT CopyOnWriteBuffer { | 
|  | public: | 
|  | // An empty buffer. | 
|  | CopyOnWriteBuffer(); | 
|  | // Share the data with an existing buffer. | 
|  | CopyOnWriteBuffer(const CopyOnWriteBuffer& buf); | 
|  | // Move contents from an existing buffer. | 
|  | CopyOnWriteBuffer(CopyOnWriteBuffer&& buf); | 
|  |  | 
|  | // Construct a buffer from a string, convenient for unittests. | 
|  | CopyOnWriteBuffer(const std::string& s); | 
|  |  | 
|  | // 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 std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | CopyOnWriteBuffer(const T* data, size_t size) | 
|  | : CopyOnWriteBuffer(data, size, size) {} | 
|  | template <typename T, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | CopyOnWriteBuffer(const T* data, size_t size, size_t capacity) | 
|  | : CopyOnWriteBuffer(size, capacity) { | 
|  | if (buffer_) { | 
|  | std::memcpy(buffer_->data(), data, size); | 
|  | offset_ = 0; | 
|  | size_ = size; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Construct a buffer from the contents of an array. | 
|  | template <typename T, | 
|  | size_t N, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | 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 std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | 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 std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | T* data() { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | if (!buffer_) { | 
|  | return nullptr; | 
|  | } | 
|  | UnshareAndEnsureCapacity(capacity()); | 
|  | return buffer_->data<T>() + offset_; | 
|  | } | 
|  |  | 
|  | // 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 std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | const T* cdata() const { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | if (!buffer_) { | 
|  | return nullptr; | 
|  | } | 
|  | return buffer_->data<T>() + offset_; | 
|  | } | 
|  |  | 
|  | size_t size() const { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | return size_; | 
|  | } | 
|  |  | 
|  | size_t capacity() const { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | return buffer_ ? buffer_->capacity() - offset_ : 0; | 
|  | } | 
|  |  | 
|  | CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | RTC_DCHECK(buf.IsConsistent()); | 
|  | if (&buf != this) { | 
|  | buffer_ = buf.buffer_; | 
|  | offset_ = buf.offset_; | 
|  | size_ = buf.size_; | 
|  | } | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | RTC_DCHECK(buf.IsConsistent()); | 
|  | buffer_ = std::move(buf.buffer_); | 
|  | offset_ = buf.offset_; | 
|  | size_ = buf.size_; | 
|  | buf.offset_ = 0; | 
|  | buf.size_ = 0; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool operator==(const CopyOnWriteBuffer& buf) const; | 
|  |  | 
|  | bool operator!=(const CopyOnWriteBuffer& buf) const { | 
|  | return !(*this == buf); | 
|  | } | 
|  |  | 
|  | uint8_t& operator[](size_t index) { | 
|  | RTC_DCHECK_LT(index, size()); | 
|  | return data()[index]; | 
|  | } | 
|  |  | 
|  | uint8_t operator[](size_t index) const { | 
|  | RTC_DCHECK_LT(index, size()); | 
|  | return cdata()[index]; | 
|  | } | 
|  |  | 
|  | // Replace the contents of the buffer. Accepts the same types as the | 
|  | // constructors. | 
|  | template <typename T, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | void SetData(const T* data, size_t size) { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | if (!buffer_) { | 
|  | buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr; | 
|  | } else if (!buffer_->HasOneRef()) { | 
|  | buffer_ = new RefCountedObject<Buffer>(data, size, capacity()); | 
|  | } else { | 
|  | buffer_->SetData(data, size); | 
|  | } | 
|  | offset_ = 0; | 
|  | size_ = size; | 
|  |  | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | size_t N, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | 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_; | 
|  | offset_ = buf.offset_; | 
|  | size_ = buf.size_; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Append data to the buffer. Accepts the same types as the constructors. | 
|  | template <typename T, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | void AppendData(const T* data, size_t size) { | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | if (!buffer_) { | 
|  | buffer_ = new RefCountedObject<Buffer>(data, size); | 
|  | offset_ = 0; | 
|  | size_ = size; | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | UnshareAndEnsureCapacity(std::max(capacity(), size_ + size)); | 
|  |  | 
|  | buffer_->SetSize(offset_ + | 
|  | size_);  // Remove data to the right of the slice. | 
|  | buffer_->AppendData(data, size); | 
|  | size_ += size; | 
|  |  | 
|  | RTC_DCHECK(IsConsistent()); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | size_t N, | 
|  | typename std::enable_if< | 
|  | internal::BufferCompat<uint8_t, T>::value>::type* = nullptr> | 
|  | 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); | 
|  |  | 
|  | // 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); | 
|  |  | 
|  | // Resets the buffer to zero size without altering capacity. Works even if the | 
|  | // buffer has been moved from. | 
|  | void Clear(); | 
|  |  | 
|  | // Swaps two buffers. | 
|  | friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) { | 
|  | std::swap(a.buffer_, b.buffer_); | 
|  | std::swap(a.offset_, b.offset_); | 
|  | std::swap(a.size_, b.size_); | 
|  | } | 
|  |  | 
|  | CopyOnWriteBuffer Slice(size_t offset, size_t length) const { | 
|  | CopyOnWriteBuffer slice(*this); | 
|  | RTC_DCHECK_LE(offset, size_); | 
|  | RTC_DCHECK_LE(length + offset, size_); | 
|  | slice.offset_ += offset; | 
|  | slice.size_ = length; | 
|  | return slice; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Create a copy of the underlying data if it is referenced from other Buffer | 
|  | // objects or there is not enough capacity. | 
|  | void UnshareAndEnsureCapacity(size_t new_capacity); | 
|  |  | 
|  | // Pre- and postcondition of all methods. | 
|  | bool IsConsistent() const { | 
|  | if (buffer_) { | 
|  | return buffer_->capacity() > 0 && offset_ <= buffer_->size() && | 
|  | offset_ + size_ <= buffer_->size(); | 
|  | } else { | 
|  | return size_ == 0 && offset_ == 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // buffer_ is either null, or points to an rtc::Buffer with capacity > 0. | 
|  | scoped_refptr<RefCountedObject<Buffer>> buffer_; | 
|  | // This buffer may represent a slice of a original data. | 
|  | size_t offset_;  // Offset of a current slice in the original data in buffer_. | 
|  | // Should be 0 if the buffer_ is empty. | 
|  | size_t size_;    // Size of a current slice in the original data in buffer_. | 
|  | // Should be 0 if the buffer_ is empty. | 
|  | }; | 
|  |  | 
|  | }  // namespace rtc | 
|  |  | 
|  | #endif  // RTC_BASE_COPY_ON_WRITE_BUFFER_H_ |