| /* |
| * 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 |