// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/flip_server/ring_buffer.h" #include "base/logging.h" namespace net { RingBuffer::RingBuffer(int buffer_size) : buffer_(new char[buffer_size]), buffer_size_(buffer_size), bytes_used_(0), read_idx_(0), write_idx_(0) { } RingBuffer::~RingBuffer() {} //////////////////////////////////////////////////////////////////////////////// int RingBuffer::ReadableBytes() const { return bytes_used_; } //////////////////////////////////////////////////////////////////////////////// int RingBuffer::BufferSize() const { return buffer_size_; } //////////////////////////////////////////////////////////////////////////////// int RingBuffer::BytesFree() const { return BufferSize() - ReadableBytes(); } //////////////////////////////////////////////////////////////////////////////// bool RingBuffer::Empty() const { return ReadableBytes() == 0; } //////////////////////////////////////////////////////////////////////////////// bool RingBuffer::Full() const { return ReadableBytes() == BufferSize(); } //////////////////////////////////////////////////////////////////////////////// // Returns the number of characters written. // Appends up-to-'size' bytes to the ringbuffer. int RingBuffer::Write(const char* bytes, int size) { CHECK_GE(size, 0); #if 1 char* wptr; int wsize; GetWritablePtr(&wptr, &wsize); int bytes_remaining = size; int bytes_written = 0; while (wsize && bytes_remaining) { if (wsize > bytes_remaining) { wsize = bytes_remaining; } memcpy(wptr, bytes + bytes_written, wsize); bytes_written += wsize; bytes_remaining -= wsize; AdvanceWritablePtr(wsize); GetWritablePtr(&wptr, &wsize); } return bytes_written; #else const char* p = bytes; int bytes_to_write = size; int bytes_available = BytesFree(); if (bytes_available < bytes_to_write) { bytes_to_write = bytes_available; } const char* end = bytes + bytes_to_write; while (p != end) { this->buffer_[this->write_idx_] = *p; ++p; ++this->write_idx_; if (this->write_idx_ >= this->buffer_size_) { this->write_idx_ = 0; } } bytes_used_ += bytes_to_write; return bytes_to_write; #endif } //////////////////////////////////////////////////////////////////////////////// // Sets *ptr to the beginning of writable memory, and sets *size to the size // available for writing using this pointer. void RingBuffer::GetWritablePtr(char** ptr, int* size) const { *ptr = buffer_.get() + write_idx_; if (bytes_used_ == buffer_size_) { *size = 0; } else if (read_idx_ > write_idx_) { *size = read_idx_ - write_idx_; } else { *size = buffer_size_ - write_idx_; } } //////////////////////////////////////////////////////////////////////////////// // Sets *ptr to the beginning of readable memory, and sets *size to the size // available for reading using this pointer. void RingBuffer::GetReadablePtr(char** ptr, int* size) const { *ptr = buffer_.get() + read_idx_; if (bytes_used_ == 0) { *size = 0; } else if (write_idx_ > read_idx_) { *size = write_idx_ - read_idx_; } else { *size = buffer_size_ - read_idx_; } } //////////////////////////////////////////////////////////////////////////////// // returns the number of bytes read into int RingBuffer::Read(char* bytes, int size) { CHECK_GE(size, 0); #if 1 char* rptr; int rsize; GetReadablePtr(&rptr, &rsize); int bytes_remaining = size; int bytes_read = 0; while (rsize && bytes_remaining) { if (rsize > bytes_remaining) { rsize = bytes_remaining; } memcpy(bytes + bytes_read, rptr, rsize); bytes_read += rsize; bytes_remaining -= rsize; AdvanceReadablePtr(rsize); GetReadablePtr(&rptr, &rsize); } return bytes_read; #else char* p = bytes; int bytes_to_read = size; int bytes_used = ReadableBytes(); if (bytes_used < bytes_to_read) { bytes_to_read = bytes_used; } char* end = bytes + bytes_to_read; while (p != end) { *p = this->buffer_[this->read_idx_]; ++p; ++this->read_idx_; if (this->read_idx_ >= this->buffer_size_) { this->read_idx_ = 0; } } this->bytes_used_ -= bytes_to_read; return bytes_to_read; #endif } //////////////////////////////////////////////////////////////////////////////// void RingBuffer::Clear() { bytes_used_ = 0; write_idx_ = 0; read_idx_ = 0; } //////////////////////////////////////////////////////////////////////////////// bool RingBuffer::Reserve(int size) { DCHECK(size > 0); char* write_ptr = NULL; int write_size = 0; GetWritablePtr(&write_ptr, &write_size); if (write_size < size) { char* read_ptr = NULL; int read_size = 0; GetReadablePtr(&read_ptr, &read_size); if (size <= BytesFree()) { // The fact that the total Free size is big enough but writable size is // not means that the writeable region is broken into two pieces: only // possible if the read_idx < write_idx. If write_idx < read_idx, then // the writeable region must be contiguous: [write_idx, read_idx). There // is no work to be done for the latter. DCHECK(read_idx_ <= write_idx_); DCHECK(read_size == ReadableBytes()); if (read_idx_ < write_idx_) { // Writeable area fragmented, consolidate it. memmove(buffer_.get(), read_ptr, read_size); read_idx_ = 0; write_idx_ = read_size; } else if (read_idx_ == write_idx_) { // No unconsumed data in the buffer, simply reset the indexes. DCHECK(ReadableBytes() == 0); read_idx_ = 0; write_idx_ = 0; } } else { Resize(ReadableBytes() + size); } } DCHECK_LE(size, buffer_size_ - write_idx_); return true; } //////////////////////////////////////////////////////////////////////////////// void RingBuffer::AdvanceReadablePtr(int amount_to_consume) { CHECK_GE(amount_to_consume, 0); if (amount_to_consume >= bytes_used_) { Clear(); return; } read_idx_ += amount_to_consume; read_idx_ %= buffer_size_; bytes_used_ -= amount_to_consume; } //////////////////////////////////////////////////////////////////////////////// void RingBuffer::AdvanceWritablePtr(int amount_to_produce) { CHECK_GE(amount_to_produce, 0); CHECK_LE(amount_to_produce, BytesFree()); write_idx_ += amount_to_produce; write_idx_ %= buffer_size_; bytes_used_ += amount_to_produce; } //////////////////////////////////////////////////////////////////////////////// void RingBuffer::Resize(int buffer_size) { CHECK_GE(buffer_size, 0); if (buffer_size == buffer_size_) return; char* new_buffer = new char[buffer_size]; if (buffer_size < bytes_used_) { // consume the oldest data. AdvanceReadablePtr(bytes_used_ - buffer_size); } int bytes_written = 0; int bytes_used = bytes_used_; while (true) { int size; char* ptr; GetReadablePtr(&ptr, &size); if (size == 0) break; if (size > buffer_size) { size = buffer_size; } memcpy(new_buffer + bytes_written, ptr, size); bytes_written += size; AdvanceReadablePtr(size); } buffer_.reset(new_buffer); buffer_size_ = buffer_size; bytes_used_ = bytes_used; read_idx_ = 0; write_idx_ = bytes_used_ % buffer_size_; } } // namespace net