Chromium Code Reviews| Index: remoting/base/compound_buffer.cc |
| diff --git a/remoting/base/compound_buffer.cc b/remoting/base/compound_buffer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..884a91b2a8f54f396c565d5f729e3fd71dfab993 |
| --- /dev/null |
| +++ b/remoting/base/compound_buffer.cc |
| @@ -0,0 +1,232 @@ |
| +// Copyright (c) 2010 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 <functional> |
| + |
| +#include "base/logging.h" |
| +#include "net/base/io_buffer.h" |
| +#include "remoting/base/compound_buffer.h" |
| + |
| +namespace remoting { |
| + |
| +CompoundBuffer::DataChunk::DataChunk( |
| + net::IOBuffer* buffer, const char* start, int size) |
| + : buffer(buffer), |
| + start(start), |
|
awong
2010/11/16 00:04:58
ick, that's confusing. Can we call the parameters
Sergey Ulanov
2010/11/16 01:25:09
Done.
|
| + size(size) { |
| +} |
| + |
| +CompoundBuffer::CompoundBuffer() |
| + : total_bytes_(0), |
| + locked_(false) { |
| +} |
| + |
| +CompoundBuffer::~CompoundBuffer() { |
| +} |
| + |
| +void CompoundBuffer::Clear() { |
| + CHECK(!locked_); |
| + chunks_.clear(); |
| + total_bytes_ = 0; |
| +} |
| + |
| +void CompoundBuffer::Append(net::IOBuffer* buffer, |
| + const char* start, int size) { |
| + // A weak check that the |start| is within |buffer|. |
| + DCHECK_GE(start, buffer->data()); |
| + DCHECK_GT(size, 0); |
| + |
| + CHECK(!locked_); |
| + |
| + chunks_.push_back(DataChunk(buffer, start, size)); |
| + total_bytes_ += size; |
| +} |
| + |
| +void CompoundBuffer::Append(net::IOBuffer* buffer, int size) { |
| + Append(buffer, buffer->data(), size); |
| +} |
| + |
| +void CompoundBuffer::Append(const CompoundBuffer& buffer) { |
| + for (DataChunkList::const_iterator it = buffer.chunks_.begin(); |
| + it != buffer.chunks_.end(); ++it) { |
| + Append(it->buffer, it->start, it->size); |
| + } |
| +} |
| + |
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, |
| + const char* start, int size) { |
| + // A weak check that the |start| is within |buffer|. |
| + DCHECK_GE(start, buffer->data()); |
| + DCHECK_GT(size, 0); |
| + |
| + CHECK(!locked_); |
| + |
| + chunks_.push_front(DataChunk(buffer, start, size)); |
| + total_bytes_ += size; |
| +} |
| + |
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, int size) { |
| + Prepend(buffer, buffer->data(), size); |
| +} |
| + |
| +void CompoundBuffer::Prepend(const CompoundBuffer& buffer) { |
| + for (DataChunkList::const_iterator it = buffer.chunks_.begin(); |
| + it != buffer.chunks_.end(); ++it) { |
| + Prepend(it->buffer, it->start, it->size); |
| + } |
| +} |
| +void CompoundBuffer::AppendCopyOf(const char* data, int size) { |
| + net::IOBuffer* buffer = new net::IOBuffer(size); |
| + memcpy(buffer->data(), data, size); |
| + Append(buffer, size); |
| +} |
| + |
| +void CompoundBuffer::PrependCopyOf(const char* data, int size) { |
| + net::IOBuffer* buffer = new net::IOBuffer(size); |
| + memcpy(buffer->data(), data, size); |
| + Prepend(buffer, size); |
| +} |
| + |
| +void CompoundBuffer::Lock() { |
| + locked_ = true; |
| +} |
| + |
| +net::IOBufferWithSize* CompoundBuffer::ToIOBufferWithSize() const { |
| + net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); |
| + CopyTo(result->data(), total_bytes_); |
| + return result; |
| +} |
| + |
| +void CompoundBuffer::CopyTo(char* data, int size) const { |
| + char* pos = data; |
| + for (DataChunkList::const_iterator it = chunks_.begin(); |
| + it != chunks_.end(); ++it) { |
| + CHECK_LE(pos + it->size, data + size); |
| + memcpy(pos, it->start, it->size); |
| + pos += it->size; |
| + } |
| +} |
| + |
| +void CompoundBuffer::CopyFrom(const CompoundBuffer& source, |
| + int start, int end) { |
| + // Check that 0 <= |start| <= |end| <= |total_bytes_|. |
| + DCHECK_LE(0, start); |
| + DCHECK_LE(start, end); |
| + DCHECK_LE(end, source.total_bytes()); |
| + |
| + Clear(); |
| + |
| + if (end == start) { |
| + return; |
| + } |
| + |
| + // Iterate over chunks in the |source| and add those that we need. |
| + int pos = 0; |
| + for (DataChunkList::const_iterator it = source.chunks_.begin(); |
| + it != source.chunks_.end(); ++it) { |
| + |
| + // Add data from the current chunk only if it is in the specified interval. |
| + if (pos + it->size > start && pos < end) { |
| + int relative_start = std::max(0, start - pos); |
| + int relative_end = std::min(it->size, end - pos); |
| + DCHECK_LE(0, relative_start); |
| + DCHECK_LT(relative_start, relative_end); |
| + DCHECK_LE(relative_end, it->size); |
| + Append(it->buffer.get(), it->start + relative_start, |
| + relative_end - relative_start); |
| + } |
| + |
| + pos += it->size; |
| + if (pos >= end) { |
| + // We've got all the data we need. |
| + break; |
| + } |
| + } |
| + |
| + DCHECK_EQ(total_bytes_, end - start); |
| +} |
| + |
| +CompoundBufferInputStream::CompoundBufferInputStream( |
| + const CompoundBuffer* buffer) |
| + : buffer_(buffer), |
| + current_chunk_(0), |
| + current_chunk_position_(0), |
| + position_(0), |
| + last_returned_size_(0) { |
| + DCHECK(buffer_->locked()); |
| +} |
| + |
| +CompoundBufferInputStream::~CompoundBufferInputStream() { |
| +} |
| + |
| +bool CompoundBufferInputStream::Next(const void** data, int* size) { |
| + if (current_chunk_ < buffer_->chunks_.size()) { |
| + // Reply with the number of bytes remaining in the current buffer. |
| + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; |
| + int read_size = chunk.size - current_chunk_position_; |
| + *data = chunk.start + current_chunk_position_; |
| + *size = read_size; |
| + |
| + // Adjust position. |
| + ++current_chunk_; |
| + current_chunk_position_ = 0; |
| + position_ += read_size; |
| + |
| + last_returned_size_ = read_size; |
| + return true; |
| + } |
| + |
| + DCHECK_EQ(position_, buffer_->total_bytes()); |
| + |
| + // We've reached the end of the stream. So reset |last_returned_size_| |
| + // to zero to prevent any backup request. |
| + // This is the same as in ArrayInputStream. |
| + // See google/protobuf/io/zero_copy_stream_impl_lite.cc. |
| + last_returned_size_ = 0; |
| + return false; |
| +} |
| + |
| +void CompoundBufferInputStream::BackUp(int count) { |
| + DCHECK_LE(count, last_returned_size_); |
| + DCHECK_GT(current_chunk_, 0u); |
| + |
| + // Rewind one buffer and rewind data offset by |count| bytes. |
| + --current_chunk_; |
| + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; |
| + current_chunk_position_ = chunk.size - count; |
| + position_ -= count; |
| + DCHECK_GE(position_, 0); |
| + |
| + // Prevent additional backups. |
| + last_returned_size_ = 0; |
| +} |
| + |
| +bool CompoundBufferInputStream::Skip(int count) { |
| + DCHECK_GE(count, 0); |
| + last_returned_size_ = 0; |
| + |
| + while (count > 0 && current_chunk_ < buffer_->chunks_.size()) { |
| + const CompoundBuffer::DataChunk& chunk = buffer_->chunks_[current_chunk_]; |
| + int read = std::min(count, chunk.size - current_chunk_position_); |
| + |
| + // Advance the current buffer offset and position. |
| + current_chunk_position_ += read; |
| + position_ += read; |
| + count -= read; |
| + |
| + // If the current buffer is fully read, then advance to the next buffer. |
| + if (current_chunk_position_ == chunk.size) { |
| + ++current_chunk_; |
| + current_chunk_position_ = 0; |
| + } |
| + } |
| + |
| + return count == 0; |
| +} |
| + |
| +int64 CompoundBufferInputStream::ByteCount() const { |
| + return position_; |
| +} |
| + |
| +} // namespace remoting |