248 lines
7.3 KiB
C++
248 lines
7.3 KiB
C++
|
/*
|
||
|
* Copyright 2019 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/memory/fifo_buffer.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
|
||
|
#include "rtc_base/thread.h"
|
||
|
|
||
|
namespace rtc {
|
||
|
|
||
|
FifoBuffer::FifoBuffer(size_t size)
|
||
|
: state_(SS_OPEN),
|
||
|
buffer_(new char[size]),
|
||
|
buffer_length_(size),
|
||
|
data_length_(0),
|
||
|
read_position_(0),
|
||
|
owner_(Thread::Current()) {
|
||
|
// all events are done on the owner_ thread
|
||
|
}
|
||
|
|
||
|
FifoBuffer::FifoBuffer(size_t size, Thread* owner)
|
||
|
: state_(SS_OPEN),
|
||
|
buffer_(new char[size]),
|
||
|
buffer_length_(size),
|
||
|
data_length_(0),
|
||
|
read_position_(0),
|
||
|
owner_(owner) {
|
||
|
// all events are done on the owner_ thread
|
||
|
}
|
||
|
|
||
|
FifoBuffer::~FifoBuffer() {}
|
||
|
|
||
|
bool FifoBuffer::GetBuffered(size_t* size) const {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
*size = data_length_;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool FifoBuffer::SetCapacity(size_t size) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
if (data_length_ > size) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (size != buffer_length_) {
|
||
|
char* buffer = new char[size];
|
||
|
const size_t copy = data_length_;
|
||
|
const size_t tail_copy = std::min(copy, buffer_length_ - read_position_);
|
||
|
memcpy(buffer, &buffer_[read_position_], tail_copy);
|
||
|
memcpy(buffer + tail_copy, &buffer_[0], copy - tail_copy);
|
||
|
buffer_.reset(buffer);
|
||
|
read_position_ = 0;
|
||
|
buffer_length_ = size;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::ReadOffset(void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t offset,
|
||
|
size_t* bytes_read) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
return ReadOffsetLocked(buffer, bytes, offset, bytes_read);
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::WriteOffset(const void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t offset,
|
||
|
size_t* bytes_written) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
return WriteOffsetLocked(buffer, bytes, offset, bytes_written);
|
||
|
}
|
||
|
|
||
|
StreamState FifoBuffer::GetState() const {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
return state_;
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::Read(void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t* bytes_read,
|
||
|
int* error) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
const bool was_writable = data_length_ < buffer_length_;
|
||
|
size_t copy = 0;
|
||
|
StreamResult result = ReadOffsetLocked(buffer, bytes, 0, ©);
|
||
|
|
||
|
if (result == SR_SUCCESS) {
|
||
|
// If read was successful then adjust the read position and number of
|
||
|
// bytes buffered.
|
||
|
read_position_ = (read_position_ + copy) % buffer_length_;
|
||
|
data_length_ -= copy;
|
||
|
if (bytes_read) {
|
||
|
*bytes_read = copy;
|
||
|
}
|
||
|
|
||
|
// if we were full before, and now we're not, post an event
|
||
|
if (!was_writable && copy > 0) {
|
||
|
PostEvent(owner_, SE_WRITE, 0);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::Write(const void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t* bytes_written,
|
||
|
int* error) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
|
||
|
const bool was_readable = (data_length_ > 0);
|
||
|
size_t copy = 0;
|
||
|
StreamResult result = WriteOffsetLocked(buffer, bytes, 0, ©);
|
||
|
|
||
|
if (result == SR_SUCCESS) {
|
||
|
// If write was successful then adjust the number of readable bytes.
|
||
|
data_length_ += copy;
|
||
|
if (bytes_written) {
|
||
|
*bytes_written = copy;
|
||
|
}
|
||
|
|
||
|
// if we didn't have any data to read before, and now we do, post an event
|
||
|
if (!was_readable && copy > 0) {
|
||
|
PostEvent(owner_, SE_READ, 0);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void FifoBuffer::Close() {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
state_ = SS_CLOSED;
|
||
|
}
|
||
|
|
||
|
const void* FifoBuffer::GetReadData(size_t* size) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
*size = (read_position_ + data_length_ <= buffer_length_)
|
||
|
? data_length_
|
||
|
: buffer_length_ - read_position_;
|
||
|
return &buffer_[read_position_];
|
||
|
}
|
||
|
|
||
|
void FifoBuffer::ConsumeReadData(size_t size) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
RTC_DCHECK(size <= data_length_);
|
||
|
const bool was_writable = data_length_ < buffer_length_;
|
||
|
read_position_ = (read_position_ + size) % buffer_length_;
|
||
|
data_length_ -= size;
|
||
|
if (!was_writable && size > 0) {
|
||
|
PostEvent(owner_, SE_WRITE, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void* FifoBuffer::GetWriteBuffer(size_t* size) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
if (state_ == SS_CLOSED) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
// if empty, reset the write position to the beginning, so we can get
|
||
|
// the biggest possible block
|
||
|
if (data_length_ == 0) {
|
||
|
read_position_ = 0;
|
||
|
}
|
||
|
|
||
|
const size_t write_position =
|
||
|
(read_position_ + data_length_) % buffer_length_;
|
||
|
*size = (write_position > read_position_ || data_length_ == 0)
|
||
|
? buffer_length_ - write_position
|
||
|
: read_position_ - write_position;
|
||
|
return &buffer_[write_position];
|
||
|
}
|
||
|
|
||
|
void FifoBuffer::ConsumeWriteBuffer(size_t size) {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
RTC_DCHECK(size <= buffer_length_ - data_length_);
|
||
|
const bool was_readable = (data_length_ > 0);
|
||
|
data_length_ += size;
|
||
|
if (!was_readable && size > 0) {
|
||
|
PostEvent(owner_, SE_READ, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool FifoBuffer::GetWriteRemaining(size_t* size) const {
|
||
|
webrtc::MutexLock lock(&mutex_);
|
||
|
*size = buffer_length_ - data_length_;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::ReadOffsetLocked(void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t offset,
|
||
|
size_t* bytes_read) {
|
||
|
if (offset >= data_length_) {
|
||
|
return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS;
|
||
|
}
|
||
|
|
||
|
const size_t available = data_length_ - offset;
|
||
|
const size_t read_position = (read_position_ + offset) % buffer_length_;
|
||
|
const size_t copy = std::min(bytes, available);
|
||
|
const size_t tail_copy = std::min(copy, buffer_length_ - read_position);
|
||
|
char* const p = static_cast<char*>(buffer);
|
||
|
memcpy(p, &buffer_[read_position], tail_copy);
|
||
|
memcpy(p + tail_copy, &buffer_[0], copy - tail_copy);
|
||
|
|
||
|
if (bytes_read) {
|
||
|
*bytes_read = copy;
|
||
|
}
|
||
|
return SR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
StreamResult FifoBuffer::WriteOffsetLocked(const void* buffer,
|
||
|
size_t bytes,
|
||
|
size_t offset,
|
||
|
size_t* bytes_written) {
|
||
|
if (state_ == SS_CLOSED) {
|
||
|
return SR_EOS;
|
||
|
}
|
||
|
|
||
|
if (data_length_ + offset >= buffer_length_) {
|
||
|
return SR_BLOCK;
|
||
|
}
|
||
|
|
||
|
const size_t available = buffer_length_ - data_length_ - offset;
|
||
|
const size_t write_position =
|
||
|
(read_position_ + data_length_ + offset) % buffer_length_;
|
||
|
const size_t copy = std::min(bytes, available);
|
||
|
const size_t tail_copy = std::min(copy, buffer_length_ - write_position);
|
||
|
const char* const p = static_cast<const char*>(buffer);
|
||
|
memcpy(&buffer_[write_position], p, tail_copy);
|
||
|
memcpy(&buffer_[0], p + tail_copy, copy - tail_copy);
|
||
|
|
||
|
if (bytes_written) {
|
||
|
*bytes_written = copy;
|
||
|
}
|
||
|
return SR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
} // namespace rtc
|