blob: be1fc302a661ee0031b830a65467277cbef38caa [file] [log] [blame]
/*
* 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.
*/
#include "rtc_base/copy_on_write_buffer.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
#include "absl/algorithm/container.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/make_ref_counted.h"
#include "api/scoped_refptr.h"
#include "rtc_base/checks.h"
#include "rtc_base/ref_counted_object.h"
namespace webrtc {
CopyOnWriteBuffer::RawBuffer::RawBuffer(size_t size)
: size_(size), data_(std::make_unique_for_overwrite<uint8_t[]>(size)) {}
scoped_refptr<CopyOnWriteBuffer::RefCountedBuffer>
CopyOnWriteBuffer::CreateBuffer(size_t capacity) {
if (capacity == 0) {
return nullptr;
}
return make_ref_counted<RefCountedBuffer>(capacity);
}
CopyOnWriteBuffer::CopyOnWriteBuffer() : offset_(0), size_(0) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf)
: buffer_(buf.buffer_), offset_(buf.offset_), size_(buf.size_) {}
CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept
: buffer_(std::move(buf.buffer_)), offset_(buf.offset_), size_(buf.size_) {
buf.offset_ = 0;
buf.size_ = 0;
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(absl::string_view s)
: CopyOnWriteBuffer(s.data(), s.length()) {}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
: buffer_(CreateBuffer(/*capacity=*/size)), offset_(0), size_(size) {
// note - the RefCountedBuffer will be created uninitialized.
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
: buffer_(CreateBuffer(std::max(size, capacity))), offset_(0), size_(size) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::~CopyOnWriteBuffer() = default;
bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const {
// Must either be the same view of the same buffer or have the same contents.
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
return size_ == buf.size_ &&
(cdata() == buf.cdata() || memcmp(cdata(), buf.cdata(), size_) == 0);
}
void CopyOnWriteBuffer::SetSize(size_t size) {
RTC_DCHECK(IsConsistent());
if (size <= size_) {
size_ = size;
return;
}
UnshareAndEnsureCapacity(std::max(capacity(), size));
size_ = size;
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::EnsureCapacity(size_t new_capacity) {
RTC_DCHECK(IsConsistent());
if (new_capacity <= capacity()) {
return;
}
UnshareAndEnsureCapacity(new_capacity);
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::Clear() {
if (!buffer_)
return;
if (!buffer_->HasOneRef()) {
buffer_ = CreateBuffer(capacity());
}
offset_ = 0;
size_ = 0;
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::Set(ArrayView<const uint8_t> data) {
RTC_DCHECK(IsConsistent());
if (data.empty()) {
offset_ = 0;
size_ = 0;
return;
}
if (buffer_ == nullptr || !buffer_->HasOneRef() ||
buffer_->capacity() < data.size()) {
buffer_ = CreateBuffer(std::max(data.size(), capacity()));
}
absl::c_copy(data, buffer_->data().begin());
offset_ = 0;
size_ = data.size();
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::Append(ArrayView<const uint8_t> data) {
RTC_DCHECK(IsConsistent());
if (data.empty()) {
return;
}
UnshareAndEnsureCapacity(std::max(capacity(), size_ + data.size()));
absl::c_copy(data, buffer_->data().subspan(offset_ + size_).begin());
size_ += data.size();
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) {
if (buffer_ != nullptr && buffer_->HasOneRef() &&
new_capacity <= capacity()) {
return;
}
scoped_refptr<RefCountedBuffer> b = CreateBuffer(new_capacity);
absl::c_copy(AsConstSpan(), b->data().begin());
offset_ = 0;
buffer_ = std::move(b);
RTC_DCHECK(IsConsistent());
}
} // namespace webrtc