| 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..9e8b8c32a2ae77d957ebe33cd68976786dc53f24
|
| --- /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_value, const char* start_value, int size_value)
|
| + : buffer(buffer_value),
|
| + start(start_value),
|
| + size(size_value) {
|
| +}
|
| +
|
| +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
|
|
|