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..8c536e46b39a41a11e7fdd9e89d942d055bba2cf |
| --- /dev/null |
| +++ b/remoting/base/compound_buffer.cc |
| @@ -0,0 +1,209 @@ |
| +// 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* data_start, int data_size) |
| + : buffer(buffer), |
| + data_start(data_start), |
| + data_size(data_size) { |
| +} |
| + |
| +CompoundBuffer::CompoundBuffer() |
| + : total_bytes_(0), |
| + current_buffer_(0), |
| + current_buffer_position_(0), |
| + position_(0), |
| + last_returned_size_(0) { |
| +} |
| + |
| +CompoundBuffer::~CompoundBuffer() { |
| +} |
| + |
| +void CompoundBuffer::Clear() { |
| + buffers_.clear(); |
| + total_bytes_ = 0; |
| + position_ = 0; |
| + last_returned_size_ = 0; |
| + current_buffer_ = 0; |
| + current_buffer_position_ = 0; |
| +} |
| + |
| + |
|
awong
2010/11/12 02:40:06
remove extra newline
Sergey Ulanov
2010/11/13 04:43:39
Done.
|
| +void CompoundBuffer::Append(net::IOBuffer* buffer, |
| + const char* data, int data_size) { |
| + // A weak check that the |data| is within |buffer|. |
| + DCHECK_GE(data, buffer->data()); |
| + DCHECK_GT(data_size, 0); |
| + |
| + DCHECK_EQ(position_, 0); // Haven't started reading. |
|
awong
2010/11/12 02:40:06
So the API contract is that once you start reading
Sergey Ulanov
2010/11/13 04:43:39
Done.
|
| + |
| + buffers_.push_back(DataChunk(buffer, data, data_size)); |
| + total_bytes_ += data_size; |
| +} |
| + |
| +void CompoundBuffer::Append(net::IOBuffer* buffer, int data_size) { |
| + Append(buffer, buffer->data(), data_size); |
| +} |
| + |
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, |
| + const char* data, int data_size) { |
| + // A weak check that the |data| is within |buffer|. |
| + DCHECK_GE(data, buffer->data()); |
|
awong
2010/11/12 02:40:06
Same comments as earlier.
Also, should there be a
Sergey Ulanov
2010/11/13 04:43:39
Added locked() returns true if content is locked.
|
| + DCHECK_GT(data_size, 0); |
| + |
| + DCHECK_EQ(position_, 0); // Haven't started reading. |
| + |
| + buffers_.push_front(DataChunk(buffer, data, data_size)); |
| + total_bytes_ += data_size; |
| +} |
| + |
| +void CompoundBuffer::Prepend(net::IOBuffer* buffer, int data_size) { |
| + Prepend(buffer, buffer->data(), data_size); |
| +} |
| + |
| +void CompoundBuffer::CopyAndAppend(const char* data, int data_size) { |
| + net::IOBuffer* buffer = new net::IOBuffer(data_size); |
| + memcpy(buffer->data(), data, data_size); |
| + Append(buffer, buffer->data(), data_size); |
| +} |
| + |
| +void CompoundBuffer::CopyAndPrepend(const char* data, int data_size) { |
| + net::IOBuffer* buffer = new net::IOBuffer(data_size); |
| + memcpy(buffer->data(), data, data_size); |
| + Prepend(buffer, buffer->data(), data_size); |
| +} |
| + |
| +net::IOBufferWithSize* CompoundBuffer::Assemble() const { |
| + net::IOBufferWithSize* result = new net::IOBufferWithSize(total_bytes_); |
|
awong
2010/11/12 02:40:06
Are these things refcounted?
Sergey Ulanov
2010/11/13 04:43:39
Yes, but we just create it, and then the caller wi
|
| + Assemble(result->data(), total_bytes_); |
| + return result; |
| +} |
| + |
| +void CompoundBuffer::Assemble(char* data, int data_size) const { |
| + char* pos = data; |
| + for (DataChunkList::const_iterator it = buffers_.begin(); |
| + it != buffers_.end(); ++it) { |
| + CHECK_LE(pos + it->data_size, data + data_size); |
| + memcpy(pos, it->data_start, it->data_size); |
| + pos += it->data_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.buffers_.begin(); |
| + it != source.buffers_.end(); ++it) { |
| + |
| + // Add data from the current chunk only if it is in the specified interval. |
| + if (pos + it->data_size > start && pos < end) { |
| + int relative_start = std::max(0, start - pos); |
| + int relative_end = std::min(it->data_size, end - pos); |
| + DCHECK_LE(0, relative_start); |
| + DCHECK_LT(relative_start, relative_end); |
| + DCHECK_LE(relative_end, it->data_size); |
| + Append(it->buffer.get(), it->data_start + relative_start, |
| + relative_end - relative_start); |
| + } |
| + |
| + pos += it->data_size; |
| + if (pos >= end) { |
| + // We've got all the data we need. |
| + break; |
| + } |
| + } |
| + |
| + DCHECK_EQ(total_bytes_, end - start); |
| +} |
| + |
| +bool CompoundBuffer::Next(const void** data, int* size) { |
| + if (current_buffer_ < buffers_.size()) { |
| + // Reply with the number of bytes remaining in the current buffer. |
| + const DataChunk& buffer = buffers_[current_buffer_]; |
| + int read_size = buffer.data_size - current_buffer_position_; |
| + *data = buffer.data_start + current_buffer_position_; |
| + *size = read_size; |
| + |
| + // Adjust position. |
| + ++current_buffer_; |
| + current_buffer_position_ = 0; |
| + position_ += read_size; |
| + |
| + last_returned_size_ = read_size; |
| + return true; |
| + } |
| + |
| + DCHECK_EQ(position_, 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 CompoundBuffer::BackUp(int count) { |
| + DCHECK_LE(count, last_returned_size_); |
| + DCHECK_GT(current_buffer_, 0u); |
| + |
| + // Rewind one buffer and rewind data offset by |count| bytes. |
| + --current_buffer_; |
| + const DataChunk& buffer = buffers_[current_buffer_]; |
| + current_buffer_position_ = buffer.data_size - count; |
| + position_ -= count; |
| + DCHECK_GE(position_, 0); |
| + |
| + // Prevent additional backups. |
| + last_returned_size_ = 0; |
| +} |
| + |
| +bool CompoundBuffer::Skip(int count) { |
| + DCHECK_GE(count, 0); |
| + last_returned_size_ = 0; |
| + |
| + while (count > 0 && current_buffer_ < buffers_.size()) { |
| + const DataChunk& buffer = buffers_[current_buffer_]; |
| + int read = std::min(count, buffer.data_size - current_buffer_position_); |
| + |
| + // Advance the current buffer offset and position. |
| + current_buffer_position_ += read; |
| + position_ += read; |
| + count -= read; |
| + |
| + // If the current buffer is fully read, then advance to the next buffer. |
| + if (current_buffer_position_ == buffer.data_size) { |
| + ++current_buffer_; |
| + current_buffer_position_ = 0; |
| + } |
| + } |
| + |
| + return count == 0; |
| +} |
| + |
| +int64 CompoundBuffer::ByteCount() const { |
| + return position_; |
| +} |
| + |
| +} // namespace remoting |